diff --git a/package.json b/package.json index 42227d0ac..47d863002 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "4.1.0", "name": "dbgate-all", "workspaces": [ - "packages/*" + "packages/*", + "plugins/*" ], "scripts": { "start:api": "yarn workspace dbgate-api start", @@ -22,6 +23,7 @@ "build:app": "cd app && yarn install && yarn build", "build:api": "yarn workspace dbgate-api build", "build:web:docker": "yarn workspace dbgate-web build", + "build:plugins:frontend": "yarn workspace dbgate-plugin-csv build:frontend && yarn workspace dbgate-plugin-excel build:frontend && yarn workspace dbgate-plugin-mysql build:frontend && yarn workspace dbgate-plugin-mssql build:frontend && yarn workspace dbgate-plugin-mongo build:frontend && yarn workspace dbgate-plugin-postgres build:frontend &&", "build:app:local": "cd app && yarn build:local", "start:app:local": "cd app && yarn start:local", "setCurrentVersion": "node setCurrentVersion", diff --git a/plugins/dbgate-plugin-csv/LICENSE b/plugins/dbgate-plugin-csv/LICENSE new file mode 100644 index 000000000..c15ede038 --- /dev/null +++ b/plugins/dbgate-plugin-csv/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jan Prochazka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/dbgate-plugin-csv/README.md b/plugins/dbgate-plugin-csv/README.md new file mode 100644 index 000000000..3bf17fecf --- /dev/null +++ b/plugins/dbgate-plugin-csv/README.md @@ -0,0 +1,60 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/JanProchazkaCz/30eur) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-csv.svg)](https://www.npmjs.com/package/dbgate-plugin-csv) + +# dbgate-plugin-csv + +CSV import/export plugin for DbGate + +## Usage without DbGate + +Export from fake object reader into CSV file. Fake object file can be replaced with other reader/writer factory functions, as described in +[dbgate-api package](https://www.npmjs.com/package/dbgate-api) + +```javascript +const dbgateApi = require('dbgate-api'); +const dbgatePluginCsv = require("dbgate-plugin-csv"); + +dbgateApi.registerPlugins(dbgatePluginCsv); + + +async function run() { + const reader = await dbgateApi.fakeObjectReader(); + const writer = await dbgatePluginCsv.shellApi.writer({ fileName: 'myfile1.csv', separator: ';' }); + await dbgateApi.copyStream(reader, writer); + + console.log('Finished job script'); +} +dbgateApi.runScript(run); + + +``` + +## Factory functions + +### shellApi.reader +Reads CSV file +```js + const dbgatePluginCsv = require("dbgate-plugin-csv"); + const reader = await dbgatePluginCsv.shellApi.reader({ + fileName: 'test.csv', + encoding: 'utf-8', + header: true, + delimiter: ',', + quoted: false, + limitRows: null + }); +``` + +### shellApi.writer +Writes CSV file +```js + const dbgatePluginCsv = require("dbgate-plugin-csv"); + const writer = await dbgatePluginCsv.shellApi.writer({ + fileName: 'test.csv', + encoding: 'utf-8', + header: true, + delimiter: ',', + quoted: false + }); +``` diff --git a/plugins/dbgate-plugin-csv/icon.svg b/plugins/dbgate-plugin-csv/icon.svg new file mode 100644 index 000000000..cfe0335e5 --- /dev/null +++ b/plugins/dbgate-plugin-csv/icon.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/plugins/dbgate-plugin-csv/package.json b/plugins/dbgate-plugin-csv/package.json new file mode 100644 index 000000000..64e96e929 --- /dev/null +++ b/plugins/dbgate-plugin-csv/package.json @@ -0,0 +1,39 @@ +{ + "name": "dbgate-plugin-csv", + "main": "dist/backend.js", + "version": "1.0.9", + "homepage": "https://github.com/dbgate/dbgate-plugin-csv", + "description": "CSV import/export plugin for DbGate", + "repository": { + "type": "git", + "url": "https://github.com/dbgate/dbgate-plugin-csv.git" + }, + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "author": "Jan Prochazka", + "license": "GPL", + "keywords": [ + "csv", + "import", + "export", + "dbgate", + "dbgateplugin" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-csv", + "plugout": "dbgate-plugout dbgate-plugin-csv", + "prepublishOnly": "yarn build" + }, + "devDependencies": { + "csv": "^5.3.2", + "dbgate-plugin-tools": "^1.0.4", + "lodash": "^4.17.20", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + } +} diff --git a/plugins/dbgate-plugin-csv/prettier.config.js b/plugins/dbgate-plugin-csv/prettier.config.js new file mode 100644 index 000000000..406484074 --- /dev/null +++ b/plugins/dbgate-plugin-csv/prettier.config.js @@ -0,0 +1,8 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-csv/src/backend/index.js b/plugins/dbgate-plugin-csv/src/backend/index.js new file mode 100644 index 000000000..460beba4e --- /dev/null +++ b/plugins/dbgate-plugin-csv/src/backend/index.js @@ -0,0 +1,13 @@ +const reader = require('./reader'); +const writer = require('./writer'); + +module.exports = { + packageName: 'dbgate-plugin-csv', + shellApi: { + reader, + writer, + }, + initialize(dbgateEnv) { + reader.initialize(dbgateEnv); + }, +}; diff --git a/plugins/dbgate-plugin-csv/src/backend/reader.js b/plugins/dbgate-plugin-csv/src/backend/reader.js new file mode 100644 index 000000000..6c07518f5 --- /dev/null +++ b/plugins/dbgate-plugin-csv/src/backend/reader.js @@ -0,0 +1,67 @@ +const zipObject = require('lodash/zipObject'); +const csv = require('csv'); +const fs = require('fs'); +const stream = require('stream'); + +let dbgateApi; +class CsvPrepareStream extends stream.Transform { + constructor({ header }) { + super({ objectMode: true }); + this.structure = null; + this.header = header; + } + _transform(chunk, encoding, done) { + if (this.structure) { + this.push( + zipObject( + this.structure.columns.map((x) => x.columnName), + chunk + ) + ); + done(); + } else { + if (this.header) { + this.structure = { + __isStreamHeader: true, + columns: chunk.map((columnName) => ({ columnName })), + }; + this.push(this.structure); + } else { + this.structure = { + __isStreamHeader: true, + columns: chunk.map((value, index) => ({ columnName: `col${index + 1}` })), + }; + this.push(this.structure); + this.push( + zipObject( + this.structure.columns.map((x) => x.columnName), + chunk + ) + ); + } + done(); + } + } +} + +async function reader({ fileName, encoding = 'utf-8', header = true, delimiter, limitRows = undefined }) { + console.log(`Reading file ${fileName}`); + const csvStream = csv.parse({ + // @ts-ignore + delimiter, + skip_lines_with_error: true, + to_line: limitRows ? limitRows + 1 : undefined, + }); + const downloadedFile = await dbgateApi.download(fileName); + const fileStream = fs.createReadStream(downloadedFile, encoding); + const csvPrepare = new CsvPrepareStream({ header }); + fileStream.pipe(csvStream); + csvStream.pipe(csvPrepare); + return csvPrepare; +} + +reader.initialize = (dbgateEnv) => { + dbgateApi = dbgateEnv.dbgateApi; +}; + +module.exports = reader; diff --git a/plugins/dbgate-plugin-csv/src/backend/writer.js b/plugins/dbgate-plugin-csv/src/backend/writer.js new file mode 100644 index 000000000..8e1dbeaff --- /dev/null +++ b/plugins/dbgate-plugin-csv/src/backend/writer.js @@ -0,0 +1,36 @@ +const csv = require('csv'); +const fs = require('fs'); +const stream = require('stream'); + +class CsvPrepareStream extends stream.Transform { + constructor({ header }) { + super({ objectMode: true }); + this.structure = null; + this.header = header; + } + _transform(chunk, encoding, done) { + if (this.structure) { + this.push(this.structure.columns.map((col) => chunk[col.columnName])); + done(); + } else { + this.structure = chunk; + if (this.header) { + this.push(chunk.columns.map((x) => x.columnName)); + } + done(); + } + } +} + +async function writer({ fileName, encoding = 'utf-8', header = true, delimiter, quoted }) { + console.log(`Writing file ${fileName}`); + const csvPrepare = new CsvPrepareStream({ header }); + const csvStream = csv.stringify({ delimiter, quoted }); + const fileStream = fs.createWriteStream(fileName, encoding); + csvPrepare.pipe(csvStream); + csvStream.pipe(fileStream); + csvPrepare['finisher'] = fileStream; + return csvPrepare; +} + +module.exports = writer; diff --git a/plugins/dbgate-plugin-csv/src/frontend/index.js b/plugins/dbgate-plugin-csv/src/frontend/index.js new file mode 100644 index 000000000..33b0f6a17 --- /dev/null +++ b/plugins/dbgate-plugin-csv/src/frontend/index.js @@ -0,0 +1,46 @@ +const fileFormat = { + packageName: 'dbgate-plugin-csv', + // file format identifier + storageType: 'csv', + // file extension without leading dot + extension: 'csv', + // human readable file format name + name: 'CSV', + // function name from backend, which contains reader factory, postfixed by package name + readerFunc: 'reader@dbgate-plugin-csv', + // function name from backend, which contains writer factory, postfixed by package name + writerFunc: 'writer@dbgate-plugin-csv', + // optional list of format arguments, which can be edited from UI + args: [ + { + type: 'select', + name: 'delimiter', + label: 'Delimiter', + options: [ + { name: 'Comma (,)', value: ',' }, + { name: 'Semicolon (;)', value: ';' }, + { name: 'Tab', value: '\t' }, + { name: 'Pipe (|)', value: '|' }, + ], + apiName: 'delimiter', + }, + { + type: 'checkbox', + name: 'quoted', + label: 'Quoted', + apiName: 'quoted', + direction: 'target', + }, + { + type: 'checkbox', + name: 'header', + label: 'Has header row', + apiName: 'header', + default: true, + }, + ], +}; + +export default { + fileFormats: [fileFormat], +}; diff --git a/plugins/dbgate-plugin-csv/webpack-backend.config.js b/plugins/dbgate-plugin-csv/webpack-backend.config.js new file mode 100644 index 000000000..e75357dff --- /dev/null +++ b/plugins/dbgate-plugin-csv/webpack-backend.config.js @@ -0,0 +1,23 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-csv/webpack-frontend.config.js b/plugins/dbgate-plugin-csv/webpack-frontend.config.js new file mode 100644 index 000000000..db07de291 --- /dev/null +++ b/plugins/dbgate-plugin-csv/webpack-frontend.config.js @@ -0,0 +1,24 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-excel/LICENSE b/plugins/dbgate-plugin-excel/LICENSE new file mode 100644 index 000000000..c15ede038 --- /dev/null +++ b/plugins/dbgate-plugin-excel/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jan Prochazka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/dbgate-plugin-excel/README.md b/plugins/dbgate-plugin-excel/README.md new file mode 100644 index 000000000..aaf7b0b28 --- /dev/null +++ b/plugins/dbgate-plugin-excel/README.md @@ -0,0 +1,52 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/JanProchazkaCz/30eur) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-excel.svg)](https://www.npmjs.com/package/dbgate-plugin-excel) + +# dbgate-plugin-excel + +MS Excel import/export plugin for DbGate + + +## Usage without DbGate + +Export from fake object reader into MS Excel file. Fake object file can be replaced with other reader/writer factory functions, as described in +[dbgate-api package](https://www.npmjs.com/package/dbgate-api) + +```javascript +const dbgateApi = require('dbgate-api'); +const dbgatePluginExcel = require("dbgate-plugin-excel"); + +dbgateApi.registerPlugins(dbgatePluginExcel); + + +async function run() { + const reader = await dbgateApi.fakeObjectReader(); + const writer = await dbgatePluginExcel.shellApi.writer({ fileName: 'myfile1.xlsx', sheetName: 'Sheet 1' }); + await dbgateApi.copyStream(reader, writer); + console.log('Finished job script'); +} +dbgateApi.runScript(run); + + +``` + +## Factory functions + +### shellApi.reader +Reads tabular data from one sheet in MS Excel file. +```js + const reader = await dbgatePluginExcel.shellApi.reader({ + fileName: 'test.xlsx', + sheetName: 'Album', + limitRows: null + }); +``` + +### shellApi.writer +Writes tabular data into MS excel file. There could be more writes into the some file in one script, if property sheetName is different. +```js + const reader = await dbgatePluginExcel.shellApi.writer({ + fileName: 'test.xlsx', + sheetName: 'Album', + }); +``` diff --git a/plugins/dbgate-plugin-excel/icon.svg b/plugins/dbgate-plugin-excel/icon.svg new file mode 100644 index 000000000..11492442b --- /dev/null +++ b/plugins/dbgate-plugin-excel/icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/plugins/dbgate-plugin-excel/package.json b/plugins/dbgate-plugin-excel/package.json new file mode 100644 index 000000000..6a07c6c7b --- /dev/null +++ b/plugins/dbgate-plugin-excel/package.json @@ -0,0 +1,39 @@ +{ + "name": "dbgate-plugin-excel", + "main": "dist/backend.js", + "version": "1.0.8", + "homepage": "https://github.com/dbgate/dbgate-plugin-excel", + "description": "MS Excel import/export plugin for DbGate", + "repository": { + "type": "git", + "url": "https://github.com/dbgate/dbgate-plugin-excel.git" + }, + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "author": "Jan Prochazka", + "license": "GPL", + "keywords": [ + "excel", + "import", + "export", + "dbgate", + "dbgateplugin" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "plugin": "yarn build && dbgate-plugin dbgate-plugin-excel", + "plugout": "dbgate-plugout dbgate-plugin-excel", + "prepublishOnly": "yarn build" + }, + "devDependencies": { + "dbgate-plugin-tools": "^1.0.0", + "lodash": "^4.17.20", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11", + "xlsx": "^0.16.8" + } +} diff --git a/plugins/dbgate-plugin-excel/prettier.config.js b/plugins/dbgate-plugin-excel/prettier.config.js new file mode 100644 index 000000000..406484074 --- /dev/null +++ b/plugins/dbgate-plugin-excel/prettier.config.js @@ -0,0 +1,8 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-excel/src/backend/index.js b/plugins/dbgate-plugin-excel/src/backend/index.js new file mode 100644 index 000000000..2a1b19239 --- /dev/null +++ b/plugins/dbgate-plugin-excel/src/backend/index.js @@ -0,0 +1,26 @@ +const xlsx = require('xlsx'); +const reader = require('./reader'); +const writer = require('./writer'); + +let dbgateApi; + +module.exports = { + packageName: 'dbgate-plugin-excel', + shellApi: { + reader, + writer, + }, + + commands: { + analyse: async ({ fileName }) => { + const downloadedFile = await dbgateApi.download(fileName); + const workbook = xlsx.readFile(downloadedFile, { bookSheets: true }); + return workbook.SheetNames; + }, + }, + initialize(dbgateEnv) { + dbgateApi = dbgateEnv.dbgateApi; + writer.initialize(dbgateEnv); + reader.initialize(dbgateEnv); + }, +}; diff --git a/plugins/dbgate-plugin-excel/src/backend/reader.js b/plugins/dbgate-plugin-excel/src/backend/reader.js new file mode 100644 index 000000000..3535e40f9 --- /dev/null +++ b/plugins/dbgate-plugin-excel/src/backend/reader.js @@ -0,0 +1,67 @@ +const xlsx = require('xlsx'); +const stream = require('stream'); +const _ = require('lodash'); + +const loadedWorkbooks = {}; +let dbgateApi; + +async function loadWorkbook(fileName) { + let workbook = loadedWorkbooks[fileName]; + if (workbook) return workbook; + console.log(`Loading excel ${fileName}`); + const downloadedFile = await dbgateApi.download(fileName); + workbook = xlsx.readFile(downloadedFile); + loadedWorkbooks[fileName] = workbook; + return workbook; +} + +async function waitForDrain(stream) { + return new Promise((resolve) => { + stream.once('drain', () => { + resolve(); + }); + }); +} + +async function reader({ fileName, sheetName, limitRows = undefined }) { + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + + const workbook = await loadWorkbook(fileName); + const sheet = workbook.Sheets[sheetName]; + + const rows = xlsx.utils.sheet_to_json(sheet, { + header: 1, + blankrows: false, + }); + const header = rows[0]; + const structure = { + __isStreamHeader: true, + columns: _.range(header.length).map((index) => ({ columnName: header[index] })), + }; + if (!pass.write(structure)) await waitForDrain(pass); + + const sendAsync = async () => { + for (let rowIndex = 1; rowIndex < rows.length; rowIndex++) { + if (limitRows && rowIndex > limitRows) break; + const row = rows[rowIndex]; + const rowData = _.fromPairs(structure.columns.map((col, index) => [col.columnName, row[index]])); + if (_.isEmpty(_.omitBy(rowData, (v) => v == null || v.toString().trim().length == 0))) continue; + if (!pass.write(rowData)) await waitForDrain(pass); + } + pass.end(); + }; + + // don't wait for sending + sendAsync(); + + return pass; +} + +reader.initialize = (dbgateEnv) => { + dbgateApi = dbgateEnv.dbgateApi; +}; + +module.exports = reader; diff --git a/plugins/dbgate-plugin-excel/src/backend/writer.js b/plugins/dbgate-plugin-excel/src/backend/writer.js new file mode 100644 index 000000000..d8f6a2096 --- /dev/null +++ b/plugins/dbgate-plugin-excel/src/backend/writer.js @@ -0,0 +1,57 @@ +const xlsx = require('xlsx'); +const stream = require('stream'); + +const writingWorkbooks = {}; + +async function saveExcelFiles() { + for (const file in writingWorkbooks) { + xlsx.writeFile(writingWorkbooks[file], file); + } +} + +function createWorkbook(fileName) { + let workbook = writingWorkbooks[fileName]; + if (workbook) return workbook; + workbook = xlsx.utils.book_new(); + writingWorkbooks[fileName] = workbook; + return workbook; +} + +class ExcelSheetWriterStream extends stream.Writable { + constructor({ fileName, sheetName }) { + super({ objectMode: true }); + this.rows = []; + this.structure = null; + this.fileName = fileName; + this.sheetName = sheetName; + } + _write(chunk, enc, next) { + if (this.structure) { + this.rows.push(this.structure.columns.map((col) => chunk[col.columnName])); + } else { + this.structure = chunk; + this.rows.push(chunk.columns.map((x) => x.columnName)); + } + + next(); + } + + _final(callback) { + const workbook = createWorkbook(this.fileName); + xlsx.utils.book_append_sheet(workbook, xlsx.utils.aoa_to_sheet(this.rows), this.sheetName || 'Sheet 1'); + callback(); + } +} + +async function writer({ fileName, sheetName }) { + return new ExcelSheetWriterStream({ + fileName, + sheetName, + }); +} + +writer.initialize = ({ dbgateApi }) => { + dbgateApi.finalizer.register(saveExcelFiles); +}; + +module.exports = writer; diff --git a/plugins/dbgate-plugin-excel/src/frontend/index.js b/plugins/dbgate-plugin-excel/src/frontend/index.js new file mode 100644 index 000000000..94d515376 --- /dev/null +++ b/plugins/dbgate-plugin-excel/src/frontend/index.js @@ -0,0 +1,68 @@ +let axios; + +function initialize(dbgateEnv) { + axios = dbgateEnv.axios; +} + +const fileFormat = { + packageName: 'dbgate-plugin-excel', + // file format identifier + storageType: 'excel', + // file extension without leading dot + extension: 'xlsx', + // human readable file format name + name: 'MS Excel', + // function name from backend, which contains reader factory, postfixed by package name + readerFunc: 'reader@dbgate-plugin-excel', + // function name from backend, which contains writer factory, postfixed by package name + writerFunc: 'writer@dbgate-plugin-excel', + + addFileToSourceList: async ({ fileName }, newSources, newValues) => { + const resp = await axios.post('plugins/command', { + command: 'analyse', + packageName: 'dbgate-plugin-excel', + args: { + fileName, + }, + }); + const sheetNames = resp.data; + for (const sheetName of sheetNames) { + newSources.push(sheetName); + newValues[`sourceFile_${sheetName}`] = { + fileName, + sheetName, + }; + } + }, + + args: [ + { + type: 'checkbox', + name: 'singleFile', + label: 'Create single file', + direction: 'target', + }, + ], + + getDefaultOutputName: (sourceName, values) => { + if (values.target_excel_singleFile) { + return sourceName; + } + return null; + }, + + getOutputParams: (sourceName, values) => { + if (values.target_excel_singleFile) { + return { + sheetName: values[`targetName_${sourceName}`] || sourceName, + fileName: 'data.xlsx', + }; + } + return null; + }, +}; + +export default { + fileFormats: [fileFormat], + initialize, +}; diff --git a/plugins/dbgate-plugin-excel/webpack-backend.config.js b/plugins/dbgate-plugin-excel/webpack-backend.config.js new file mode 100644 index 000000000..e75357dff --- /dev/null +++ b/plugins/dbgate-plugin-excel/webpack-backend.config.js @@ -0,0 +1,23 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-excel/webpack-frontend.config.js b/plugins/dbgate-plugin-excel/webpack-frontend.config.js new file mode 100644 index 000000000..db07de291 --- /dev/null +++ b/plugins/dbgate-plugin-excel/webpack-frontend.config.js @@ -0,0 +1,24 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mongo/LICENSE b/plugins/dbgate-plugin-mongo/LICENSE new file mode 100644 index 000000000..bb0e9add4 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 DbGate + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/dbgate-plugin-mongo/README.md b/plugins/dbgate-plugin-mongo/README.md new file mode 100644 index 000000000..133cbb7f2 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/README.md @@ -0,0 +1,6 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-mongo.svg)](https://www.npmjs.com/package/dbgate-plugin-mongo) + +# dbgate-plugin-mongo + +Use DbGate for install of this plugin diff --git a/plugins/dbgate-plugin-mongo/icon.svg b/plugins/dbgate-plugin-mongo/icon.svg new file mode 100644 index 000000000..90fe17b70 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/icon.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/plugins/dbgate-plugin-mongo/package.json b/plugins/dbgate-plugin-mongo/package.json new file mode 100644 index 000000000..33cac471f --- /dev/null +++ b/plugins/dbgate-plugin-mongo/package.json @@ -0,0 +1,36 @@ +{ + "name": "dbgate-plugin-mongo", + "main": "dist/backend.js", + "version": "1.0.1", + "license": "MIT", + "author": "Jan Prochazka", + "homepage": "https://github.com/dbgate/dbgate-plugin-mongo", + "description": "MongoDB connect plugin for DbGate", + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "keywords": [ + "dbgate", + "dbgateplugin", + "mongo", + "mongodb" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-mongo", + "plugout": "dbgate-plugout dbgate-plugin-mongo", + "prepublishOnly": "yarn build" + }, + "devDependencies": { + "byline": "^5.0.0", + "dbgate-plugin-tools": "^1.0.4", + "dbgate-tools": "^4.1.0-rc.1", + "is-promise": "^4.0.0", + "mongodb": "^3.6.5", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + } +} diff --git a/plugins/dbgate-plugin-mongo/prettier.config.js b/plugins/dbgate-plugin-mongo/prettier.config.js new file mode 100644 index 000000000..406484074 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/prettier.config.js @@ -0,0 +1,8 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-mongo/src/backend/Analyser.js b/plugins/dbgate-plugin-mongo/src/backend/Analyser.js new file mode 100644 index 000000000..667a895df --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/backend/Analyser.js @@ -0,0 +1,24 @@ +const { DatabaseAnalyser } = require('dbgate-tools'); + +class Analyser extends DatabaseAnalyser { + constructor(pool, driver) { + super(pool, driver); + } + + async _runAnalysis() { + const collections = await this.pool.__getDatabase().listCollections().toArray(); + + const res = this.mergeAnalyseResult( + { + collections: collections.map((x) => ({ + pureName: x.name, + })), + }, + (x) => x.pureName + ); + // console.log('MERGED', res); + return res; + } +} + +module.exports = Analyser; diff --git a/plugins/dbgate-plugin-mongo/src/backend/createBulkInsertStream.js b/plugins/dbgate-plugin-mongo/src/backend/createBulkInsertStream.js new file mode 100644 index 000000000..a5257a272 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/backend/createBulkInsertStream.js @@ -0,0 +1,58 @@ +export function createBulkInsertStream(driver, stream, pool, name, options) { + const collectionName = name.pureName; + const db = pool.__getDatabase(); + + const writable = new stream.Writable({ + objectMode: true, + }); + + writable.buffer = []; + writable.wasHeader = false; + + writable.addRow = (row) => { + if (!writable.wasHeader) { + writable.wasHeader = true; + if (row.__isStreamHeader || + // TODO remove isArray test + Array.isArray(row.columns)) return; + } + writable.buffer.push(row); + }; + + writable.checkStructure = async () => { + if (options.dropIfExists || options.truncate) { + console.log(`Dropping collection ${collectionName}`); + await db.collection(collectionName).drop(); + } + if (options.truncate) { + console.log(`Truncating collection ${collectionName}`); + await db.collection(collectionName).deleteMany({}); + } + }; + + writable.send = async () => { + const rows = writable.buffer; + writable.buffer = []; + + await db.collection(collectionName).insertMany(rows); + }; + + writable.sendIfFull = async () => { + if (writable.buffer.length > 100) { + await writable.send(); + } + }; + + writable._write = async (chunk, encoding, callback) => { + writable.addRow(chunk); + await writable.sendIfFull(); + callback(); + }; + + writable._final = async (callback) => { + await writable.send(); + callback(); + }; + + return writable; +} diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js new file mode 100644 index 000000000..9379c8892 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -0,0 +1,260 @@ +const _ = require('lodash'); +const stream = require('stream'); +const isPromise = require('is-promise'); +const driverBase = require('../frontend/driver'); +const Analyser = require('./Analyser'); +const MongoClient = require('mongodb').MongoClient; +const ObjectId = require('mongodb').ObjectId; +const Cursor = require('mongodb').Cursor; +const { createBulkInsertStream } = require('./createBulkInsertStream'); + +function readCursor(cursor, options) { + return new Promise((resolve) => { + options.recordset({ __isDynamicStructure: true }); + + cursor.on('data', (data) => options.row(data)); + cursor.on('end', () => resolve()); + }); +} + +const mongoIdRegex = /^[0-9a-f]{24}$/; +function convertCondition(condition) { + if (condition && _.isString(condition._id) && condition._id.match(mongoIdRegex)) { + return { + _id: ObjectId(condition._id), + }; + } + return condition; +} + +function findArrayResult(resValue) { + if (!_.isPlainObject(resValue)) return null; + const arrays = _.values(resValue).filter((x) => _.isArray(x)); + if (arrays.length == 1) return arrays[0]; + return null; +} + +async function getScriptableDb(pool) { + const db = pool.__getDatabase(); + const collections = await db.listCollections().toArray(); + for (const collection of collections) { + db[collection.name] = db.collection(collection.name); + } + return db; +} + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + analyserClass: Analyser, + async connect({ server, port, user, password, database, useDatabaseUrl, databaseUrl, ssl }) { + // let mongoUrl = databaseUrl; + // if (!useDatabaseUrl) { + // mongoUrl = user ? `mongodb://${user}:${password}@${server}:${port}` : `mongodb://${server}:${port}`; + // if (database) mongoUrl += '/' + database; + // } + const mongoUrl = useDatabaseUrl + ? databaseUrl + : user + ? `mongodb://${user}:${password}@${server}:${port}` + : `mongodb://${server}:${port}`; + + const options = {}; + if (ssl) { + options.tls = true; + options.tlsCAFile = ssl.ca; + options.tlsCertificateKeyFile = ssl.cert || ssl.key; + options.tlsCertificateKeyFilePassword = ssl.password; + options.tlsAllowInvalidCertificates = !ssl.rejectUnauthorized; + } + + const pool = new MongoClient(mongoUrl, options); + await pool.connect(); + // const pool = await MongoClient.connect(mongoUrl); + pool.__getDatabase = database ? () => pool.db(database) : () => pool.db(); + pool.__databaseName = database; + return pool; + }, + // @ts-ignore + async query(pool, sql) { + return { + rows: [], + columns: [], + }; + }, + async stream(pool, sql, options) { + let func; + try { + func = eval(`(db,ObjectId) => ${sql}`); + } catch (err) { + options.info({ + message: 'Error compiling expression: ' + err.message, + time: new Date(), + severity: 'error', + }); + options.done(); + return; + } + const db = await getScriptableDb(pool); + + let exprValue; + try { + exprValue = func(db, ObjectId); + } catch (err) { + options.info({ + message: 'Error evaluating expression: ' + err.message, + time: new Date(), + severity: 'error', + }); + options.done(); + return; + } + + if (exprValue instanceof Cursor) { + await readCursor(exprValue, options); + } else if (isPromise(exprValue)) { + try { + const resValue = await exprValue; + + options.info({ + message: 'Command succesfully executed', + time: new Date(), + severity: 'info', + }); + options.info({ + message: JSON.stringify(resValue), + time: new Date(), + severity: 'info', + }); + + const arrayRes = findArrayResult(resValue); + if (arrayRes) { + options.recordset({ __isDynamicStructure: true }); + for (const row of arrayRes) { + options.row(row); + } + } + } catch (err) { + options.info({ + message: 'Error when running command: ' + err.message, + time: new Date(), + severity: 'error', + }); + } + } + + options.done(); + }, + async readQuery(pool, sql, structure) { + try { + const json = JSON.parse(sql); + if (json && json.pureName) { + sql = `db.${json.pureName}.find()`; + } + } catch (err) { + // query is not JSON serialized collection name + } + + // const pass = new stream.PassThrough({ + // objectMode: true, + // highWaterMark: 100, + // }); + + func = eval(`(db,ObjectId) => ${sql}`); + const db = await getScriptableDb(pool); + exprValue = func(db, ObjectId); + + // return directly stream without header row + return exprValue; + + // pass.write(structure || { __isDynamicStructure: true }); + // exprValue.on('data', (row) => pass.write(row)); + // exprValue.on('end', () => pass.end()); + + // return pass; + }, + async writeTable(pool, name, options) { + return createBulkInsertStream(this, stream, pool, name, options); + }, + async getVersion(pool) { + const status = await pool.__getDatabase().admin().serverInfo(); + return status; + }, + async listDatabases(pool) { + const res = await pool.__getDatabase().admin().listDatabases(); + return res.databases; + }, + async readCollection(pool, options) { + try { + const collection = pool.__getDatabase().collection(options.pureName); + if (options.countDocuments) { + const count = await collection.countDocuments(options.condition || {}); + return { count }; + } else { + let cursor = await collection.find(options.condition || {}); + if (options.sort) cursor = cursor.sort(options.sort); + if (options.skip) cursor = cursor.skip(options.skip); + if (options.limit) cursor = cursor.limit(options.limit); + const rows = await cursor.toArray(); + return { rows }; + } + } catch (err) { + return { errorMessage: err.message }; + } + }, + async updateCollection(pool, changeSet) { + const res = { + inserted: [], + updated: [], + deleted: [], + replaced: [], + }; + try { + const db = pool.__getDatabase(); + for (const insert of changeSet.inserts) { + const collection = db.collection(insert.pureName); + const document = { + ...insert.document, + ...insert.fields, + }; + const resdoc = await collection.insert(document); + res.inserted.push(resdoc._id); + } + for (const update of changeSet.updates) { + const collection = db.collection(update.pureName); + if (update.document) { + const document = { + ...update.document, + ...update.fields, + }; + const doc = await collection.findOne(convertCondition(update.condition)); + if (doc) { + const resdoc = await collection.replaceOne(convertCondition(update.condition), { + ...document, + _id: doc._id, + }); + res.replaced.push(resdoc._id); + } + } else { + const resdoc = await collection.updateOne(convertCondition(update.condition), { $set: update.fields }); + res.updated.push(resdoc._id); + } + } + for (const del of changeSet.deletes) { + const collection = db.collection(del.pureName); + const resdoc = await collection.deleteOne(convertCondition(del.condition)); + res.deleted.push(resdoc._id); + } + return res; + } catch (err) { + return { errorMessage: err.message }; + } + }, + + async createDatabase(pool, name) { + const db = pool.db(name); + await db.createCollection('collection1'); + }, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mongo/src/backend/index.js b/plugins/dbgate-plugin-mongo/src/backend/index.js new file mode 100644 index 000000000..34eb83d74 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/backend/index.js @@ -0,0 +1,6 @@ +const driver = require('./driver'); + +module.exports = { + packageName: 'dbgate-plugin-mongo', + driver, +}; diff --git a/plugins/dbgate-plugin-mongo/src/frontend/Dumper.js b/plugins/dbgate-plugin-mongo/src/frontend/Dumper.js new file mode 100644 index 000000000..afcc64731 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/frontend/Dumper.js @@ -0,0 +1,6 @@ +const { SqlDumper } = require('dbgate-tools'); + +class Dumper extends SqlDumper { +} + +module.exports = Dumper; diff --git a/plugins/dbgate-plugin-mongo/src/frontend/driver.js b/plugins/dbgate-plugin-mongo/src/frontend/driver.js new file mode 100644 index 000000000..9dfd38cd4 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/frontend/driver.js @@ -0,0 +1,76 @@ +const { driverBase } = require('dbgate-tools'); +const Dumper = require('./Dumper'); + +const mongoIdRegex = /^[0-9a-f]{24}$/; + +function getConditionPreview(condition) { + if (condition && _.isString(condition._id) && condition._id.match(mongoIdRegex)) { + return `{ _id: ObjectId('${condition._id}') }`; + } + return JSON.stringify(condition); +} + +/** @type {import('dbgate-types').SqlDialect} */ +const dialect = { + limitSelect: true, + rangeSelect: true, + offsetFetchRangeSyntax: true, + stringEscapeChar: "'", + fallbackDataType: 'nvarchar(max)', + nosql: true, + quoteIdentifier(s) { + return `[${s}]`; + }, +}; + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + dumperClass: Dumper, + dialect, + engine: 'mongo@dbgate-plugin-mongo', + title: 'MongoDB', + defaultPort: 27017, + supportsDatabaseUrl: true, + databaseUrlPlaceholder: 'e.g. mongodb://username:password@mongodb.mydomain.net/dbname', + + getCollectionUpdateScript(changeSet) { + let res = ''; + for (const insert of changeSet.inserts) { + res += `db.${insert.pureName}.insert(${JSON.stringify( + { + ...insert.document, + ...insert.fields, + }, + undefined, + 2 + )});\n`; + } + for (const update of changeSet.updates) { + if (update.document) { + res += `db.${update.pureName}.replaceOne(${getConditionPreview(update.condition)}, ${JSON.stringify( + { + ...update.document, + ...update.fields, + }, + undefined, + 2 + )});\n`; + } else { + res += `db.${update.pureName}.updateOne(${getConditionPreview(update.condition)}, ${JSON.stringify( + { + $set: update.fields, + }, + undefined, + 2 + )});\n`; + } + } + for (const del of changeSet.deletes) { + res += `db.${del.pureName}.deleteOne(${getConditionPreview(del.condition)});\n`; + } + return res; + }, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mongo/src/frontend/index.js b/plugins/dbgate-plugin-mongo/src/frontend/index.js new file mode 100644 index 000000000..b8b61e8d6 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/src/frontend/index.js @@ -0,0 +1,6 @@ +import driver from './driver'; + +export default { + packageName: 'dbgate-plugin-mongo', + driver, +}; diff --git a/plugins/dbgate-plugin-mongo/webpack-backend.config.js b/plugins/dbgate-plugin-mongo/webpack-backend.config.js new file mode 100644 index 000000000..e75357dff --- /dev/null +++ b/plugins/dbgate-plugin-mongo/webpack-backend.config.js @@ -0,0 +1,23 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mongo/webpack-frontend.config.js b/plugins/dbgate-plugin-mongo/webpack-frontend.config.js new file mode 100644 index 000000000..db07de291 --- /dev/null +++ b/plugins/dbgate-plugin-mongo/webpack-frontend.config.js @@ -0,0 +1,24 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mssql/LICENSE b/plugins/dbgate-plugin-mssql/LICENSE new file mode 100644 index 000000000..c15ede038 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jan Prochazka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/dbgate-plugin-mssql/README.md b/plugins/dbgate-plugin-mssql/README.md new file mode 100644 index 000000000..bac440d28 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/README.md @@ -0,0 +1,7 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/JanProchazkaCz/30eur) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-mssql.svg)](https://www.npmjs.com/package/dbgate-plugin-mssql) + +# dbgate-plugin-mssql + +MS SQL connector plugin for DbGate diff --git a/plugins/dbgate-plugin-mssql/icon.svg b/plugins/dbgate-plugin-mssql/icon.svg new file mode 100644 index 000000000..50644e26c --- /dev/null +++ b/plugins/dbgate-plugin-mssql/icon.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/dbgate-plugin-mssql/package.json b/plugins/dbgate-plugin-mssql/package.json new file mode 100644 index 000000000..a0e2db43e --- /dev/null +++ b/plugins/dbgate-plugin-mssql/package.json @@ -0,0 +1,39 @@ +{ + "name": "dbgate-plugin-mssql", + "main": "dist/backend.js", + "version": "1.2.2", + "homepage": "https://github.com/dbgate/dbgate-plugin-mssql", + "description": "MS SQL connect plugin for DbGate", + "repository": { + "type": "git", + "url": "https://github.com/dbgate/dbgate-plugin-mssql.git" + }, + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "author": "Jan Prochazka", + "license": "GPL", + "keywords": [ + "sql", + "mssql", + "dbgate", + "dbgateplugin" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "prepublishOnly": "yarn build", + "plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-mssql", + "plugout": "dbgate-plugout dbgate-plugin-mssql" + }, + "devDependencies": { + "async-lock": "^1.2.6", + "dbgate-plugin-tools": "^1.0.4", + "dbgate-tools": "^4.0.3-rc.1", + "tedious": "^9.2.3", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + } +} diff --git a/plugins/dbgate-plugin-mssql/prettier.config.js b/plugins/dbgate-plugin-mssql/prettier.config.js new file mode 100644 index 000000000..c05d71875 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/prettier.config.js @@ -0,0 +1,9 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + arrowParens: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js new file mode 100644 index 000000000..e7f700423 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -0,0 +1,208 @@ +const fp = require('lodash/fp'); +const _ = require('lodash'); +const sql = require('./sql'); + +const { DatabaseAnalyser } = require('dbgate-tools'); +const { isTypeString, isTypeNumeric } = require('dbgate-tools'); + +function objectTypeToField(type) { + switch (type.trim()) { + case 'U': + return 'tables'; + case 'V': + return 'views'; + case 'P': + return 'procedures'; + case 'IF': + case 'FN': + case 'TF': + return 'functions'; + case 'TR': + return 'triggers'; + default: + return null; + } +} + +function getColumnInfo({ + isNullable, + isIdentity, + columnName, + dataType, + charMaxLength, + numericPrecision, + numericScale, +}) { + let fullDataType = dataType; + if (charMaxLength && isTypeString(dataType)) fullDataType = `${dataType}(${charMaxLength})`; + if (numericPrecision && numericScale && isTypeNumeric(dataType)) + fullDataType = `${dataType}(${numericPrecision},${numericScale})`; + return { + columnName, + dataType: fullDataType, + notNull: !isNullable, + autoIncrement: !!isIdentity, + }; +} + +class MsSqlAnalyser extends DatabaseAnalyser { + constructor(pool, driver) { + super(pool, driver); + this.singleObjectId = null; + } + + createQuery(resFileName, typeFields) { + let res = sql[resFileName]; + if (this.singleObjectFilter) { + const { typeField } = this.singleObjectFilter; + if (!this.singleObjectId) return null; + if (!typeFields || !typeFields.includes(typeField)) return null; + return res.replace('=[OBJECT_ID_CONDITION]', ` = ${this.singleObjectId}`); + } + if (!this.modifications || !typeFields || this.modifications.length == 0) { + res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); + } else { + const filterIds = this.modifications + .filter((x) => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change')) + .map((x) => x.objectId); + if (filterIds.length == 0) { + res = res.replace('=[OBJECT_ID_CONDITION]', ' = 0'); + } else { + res = res.replace('=[OBJECT_ID_CONDITION]', ` in (${filterIds.join(',')})`); + } + } + return res; + } + + async getSingleObjectId() { + if (this.singleObjectFilter) { + const { schemaName, pureName, typeField } = this.singleObjectFilter; + const fullName = schemaName ? `[${schemaName}].[${pureName}]` : pureName; + const resId = await this.driver.query(this.pool, `SELECT OBJECT_ID('${fullName}') AS id`); + this.singleObjectId = resId.rows[0].id; + } + } + + async _runAnalysis() { + await this.getSingleObjectId(); + const tablesRows = await this.driver.query(this.pool, this.createQuery('tables', ['tables'])); + const columnsRows = await this.driver.query(this.pool, this.createQuery('columns', ['tables'])); + const pkColumnsRows = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); + const fkColumnsRows = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); + const schemaRows = await this.driver.query(this.pool, this.createQuery('getSchemas')); + + const schemas = schemaRows.rows; + + const sqlCodeRows = await this.driver.query( + this.pool, + this.createQuery('loadSqlCode', ['views', 'procedures', 'functions', 'triggers']) + ); + const getCreateSql = (row) => + sqlCodeRows.rows + .filter((x) => x.pureName == row.pureName && x.schemaName == row.schemaName) + .map((x) => x.codeText) + .join(''); + const viewsRows = await this.driver.query(this.pool, this.createQuery('views', ['views'])); + const programmableRows = await this.driver.query( + this.pool, + this.createQuery('programmables', ['procedures', 'functions']) + ); + const viewColumnRows = await this.driver.query(this.pool, this.createQuery('viewColumns', ['views'])); + + const tables = tablesRows.rows.map((row) => ({ + ...row, + columns: columnsRows.rows.filter((col) => col.objectId == row.objectId).map(getColumnInfo), + primaryKey: DatabaseAnalyser.extractPrimaryKeys(row, pkColumnsRows.rows), + foreignKeys: DatabaseAnalyser.extractForeignKeys(row, fkColumnsRows.rows), + })); + + const views = viewsRows.rows.map((row) => ({ + ...row, + createSql: getCreateSql(row), + columns: viewColumnRows.rows.filter((col) => col.objectId == row.objectId).map(getColumnInfo), + })); + + const procedures = programmableRows.rows + .filter((x) => x.sqlObjectType.trim() == 'P') + .map((row) => ({ + ...row, + createSql: getCreateSql(row), + })); + + const functions = programmableRows.rows + .filter((x) => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim())) + .map((row) => ({ + ...row, + createSql: getCreateSql(row), + })); + + return this.mergeAnalyseResult({ + tables, + views, + procedures, + functions, + schemas, + }); + } + + getDeletedObjectsForField(idArray, objectTypeField) { + return this.structure[objectTypeField] + .filter((x) => !idArray.includes(x.objectId)) + .map((x) => ({ + oldName: _.pick(x, ['schemaName', 'pureName']), + objectId: x.objectId, + action: 'remove', + objectTypeField, + })); + } + + getDeletedObjects(idArray) { + return [ + ...this.getDeletedObjectsForField(idArray, 'tables'), + ...this.getDeletedObjectsForField(idArray, 'views'), + ...this.getDeletedObjectsForField(idArray, 'procedures'), + ...this.getDeletedObjectsForField(idArray, 'functions'), + ...this.getDeletedObjectsForField(idArray, 'triggers'), + ]; + } + + async getModifications() { + const modificationsQueryData = await this.driver.query(this.pool, this.createQuery('modifications')); + // console.log('MOD - SRC', modifications); + // console.log( + // 'MODs', + // this.structure.tables.map((x) => x.modifyDate) + // ); + const modifications = modificationsQueryData.rows.map((x) => { + const { type, objectId, modifyDate, schemaName, pureName } = x; + const field = objectTypeToField(type); + if (!this.structure[field]) return null; + // @ts-ignore + const obj = this.structure[field].find((x) => x.objectId == objectId); + + // object not modified + if (obj && Math.abs(new Date(modifyDate).getTime() - new Date(obj.modifyDate).getTime()) < 1000) return null; + + /** @type {import('dbgate-types').DatabaseModification} */ + const action = obj + ? { + newName: { schemaName, pureName }, + oldName: _.pick(obj, ['schemaName', 'pureName']), + action: 'change', + objectTypeField: field, + objectId, + } + : { + newName: { schemaName, pureName }, + action: 'add', + objectTypeField: field, + objectId, + }; + return action; + }); + + return [..._.compact(modifications), ...this.getDeletedObjects(modificationsQueryData.rows.map((x) => x.objectId))]; + } +} + +module.exports = MsSqlAnalyser; diff --git a/plugins/dbgate-plugin-mssql/src/backend/createNativeBulkInsertStream.js b/plugins/dbgate-plugin-mssql/src/backend/createNativeBulkInsertStream.js new file mode 100644 index 000000000..03930bf0f --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/createNativeBulkInsertStream.js @@ -0,0 +1,34 @@ +const { createBulkInsertStreamBase } = require('dbgate-tools'); + +function runBulkInsertBatch(pool, tableName, writable, rows) { + return new Promise((resolve, reject) => { + const tableMgr = pool.tableMgr(); + tableMgr.bind(tableName, bulkMgr => { + bulkMgr.insertRows(rows, err => { + if (err) reject(err); + resolve(); + }); + }); + }); +} + +/** + * + * @param {import('dbgate-types').EngineDriver} driver + */ +function createNativeBulkInsertStream(driver, stream, pool, name, options) { + const writable = createBulkInsertStreamBase(driver, stream, pool, name, options); + + const fullName = name.schemaName ? `[${name.schemaName}].[${name.pureName}]` : name.pureName; + + writable.send = async () => { + const rows = writable.buffer; + writable.buffer = []; + + await runBulkInsertBatch(pool, fullName, writable, rows); + }; + + return writable; +} + +module.exports = createNativeBulkInsertStream; diff --git a/plugins/dbgate-plugin-mssql/src/backend/createTediousBulkInsertStream.js b/plugins/dbgate-plugin-mssql/src/backend/createTediousBulkInsertStream.js new file mode 100644 index 000000000..9dfbd00d5 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/createTediousBulkInsertStream.js @@ -0,0 +1,70 @@ +const { createBulkInsertStreamBase } = require('dbgate-tools'); +const tedious = require('tedious'); +const getConcreteType = require('./getConcreteType'); + +function runBulkInsertBatch(pool, tableName, writable, rows) { + return new Promise((resolve, reject) => { + var options = { keepNulls: true }; + + // instantiate - provide the table where you'll be inserting to, options and a callback + var bulkLoad = pool.newBulkLoad(tableName, options, (error, rowCount) => { + if (error) reject(error); + else resolve(); + }); + + for (const column of writable.columnNames) { + const tcol = writable.templateColumns.find((x) => x.columnName == column); + + bulkLoad.addColumn( + column, + tcol + ? getConcreteType(tcol.driverNativeColumn.type, tcol.driverNativeColumn.dataLength) + : tedious.TYPES.NVarChar, + { + nullable: tcol ? !tcol.notNull : true, + length: tcol ? tcol.driverNativeColumn.dataLength : undefined, + precision: tcol ? tcol.driverNativeColumn.precision : undefined, + scale: tcol ? tcol.driverNativeColumn.scale : undefined, + } + ); + } + + for (const row of rows) { + bulkLoad.addRow(row); + } + + pool.execBulkLoad(bulkLoad); + }); +} + +/** + * + * @param {import('dbgate-types').EngineDriver} driver + */ +function createTediousBulkInsertStream(driver, stream, pool, name, options) { + const writable = createBulkInsertStreamBase(driver, stream, pool, name, options); + + const fullName = name.schemaName ? `[${name.schemaName}].[${name.pureName}]` : name.pureName; + + writable.send = async () => { + if (!writable.templateColumns) { + const fullNameQuoted = name.schemaName + ? `${driver.dialect.quoteIdentifier(name.schemaName)}.${driver.dialect.quoteIdentifier(name.pureName)}` + : driver.dialect.quoteIdentifier(name.pureName); + + const respTemplate = await driver.query(pool, `SELECT * FROM ${fullNameQuoted} WHERE 1=0`, { + addDriverNativeColumn: true, + }); + writable.templateColumns = respTemplate.columns; + } + + const rows = writable.buffer; + writable.buffer = []; + + await runBulkInsertBatch(pool, fullName, writable, rows); + }; + + return writable; +} + +module.exports = createTediousBulkInsertStream; diff --git a/plugins/dbgate-plugin-mssql/src/backend/driver.js b/plugins/dbgate-plugin-mssql/src/backend/driver.js new file mode 100644 index 000000000..8be12fe70 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/driver.js @@ -0,0 +1,98 @@ +const _ = require('lodash'); +const stream = require('stream'); +const driverBase = require('../frontend/driver'); +const MsSqlAnalyser = require('./MsSqlAnalyser'); +const createTediousBulkInsertStream = require('./createTediousBulkInsertStream'); +const createNativeBulkInsertStream = require('./createNativeBulkInsertStream'); +const AsyncLock = require('async-lock'); +const nativeDriver = require('./nativeDriver'); +const lock = new AsyncLock(); +const { tediousConnect, tediousQueryCore, tediousReadQuery, tediousStream } = require('./tediousDriver'); +const { nativeConnect, nativeQueryCore, nativeReadQuery, nativeStream } = nativeDriver; +let msnodesqlv8; + +const windowsAuthTypes = [ + { + title: 'Windows', + name: 'sspi', + disabledFields: ['password', 'port', 'user'], + }, + { + title: 'SQL Server', + name: 'sql', + disabledFields: ['port'], + }, + { + title: 'Tedious driver', + name: 'tedious', + }, +]; + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + analyserClass: MsSqlAnalyser, + + getAuthTypes() { + return msnodesqlv8 ? windowsAuthTypes : null; + }, + + async connect(conn) { + const { authType } = conn; + if (msnodesqlv8 && (authType == 'sspi' || authType == 'sql')) { + return nativeConnect(conn); + } + + return tediousConnect(conn); + }, + async queryCore(pool, sql, options) { + if (pool._connectionType == 'msnodesqlv8') { + return nativeQueryCore(pool, sql, options); + } else { + return tediousQueryCore(pool, sql, options); + } + }, + async query(pool, sql, options) { + return lock.acquire('connection', async () => { + return this.queryCore(pool, sql, options); + }); + }, + async stream(pool, sql, options) { + if (pool._connectionType == 'msnodesqlv8') { + return nativeStream(pool, sql, options); + } else { + return tediousStream(pool, sql, options); + } + }, + async readQuery(pool, sql, structure) { + if (pool._connectionType == 'msnodesqlv8') { + return nativeReadQuery(pool, sql, structure); + } else { + return tediousReadQuery(pool, sql, structure); + } + }, + async writeTable(pool, name, options) { + if (pool._connectionType == 'msnodesqlv8') { + return createNativeBulkInsertStream(this, stream, pool, name, options); + } else { + return createTediousBulkInsertStream(this, stream, pool, name, options); + } + }, + async getVersion(pool) { + const { version } = (await this.query(pool, 'SELECT @@VERSION AS version')).rows[0]; + return { version }; + }, + async listDatabases(pool) { + const { rows } = await this.query(pool, 'SELECT name FROM sys.databases order by name'); + return rows; + }, +}; + +driver.initialize = dbgateEnv => { + if (dbgateEnv.nativeModules && dbgateEnv.nativeModules.msnodesqlv8) { + msnodesqlv8 = dbgateEnv.nativeModules.msnodesqlv8(); + } + nativeDriver.initialize(dbgateEnv); +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mssql/src/backend/getConcreteType.js b/plugins/dbgate-plugin-mssql/src/backend/getConcreteType.js new file mode 100644 index 000000000..c82f3bfba --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/getConcreteType.js @@ -0,0 +1,41 @@ +const tedious = require('tedious'); + +const { TYPES } = tedious; + +const N_TYPES = { + BitN: 0x68, + DateTimeN: 0x6f, + DecimalN: 0x6a, + FloatN: 0x6d, + IntN: 0x26, + MoneyN: 0x6e, + NumericN: 0x6c, +}; + +function getConcreteType(type, length) { + switch (type.id) { + case N_TYPES.BitN: + return TYPES.Bit; + case N_TYPES.NumericN: + return TYPES.Numeric; + case N_TYPES.DecimalN: + return TYPES.Decimal; + case N_TYPES.IntN: + if (length === 8) return TYPES.BigInt; + if (length === 4) return TYPES.Int; + if (length === 2) return TYPES.SmallInt; + return TYPES.TinyInt; + case N_TYPES.FloatN: + if (length === 8) return TYPES.Float; + return TYPES.Real; + case N_TYPES.MoneyN: + if (length === 8) return TYPES.Money; + return TYPES.SmallMoney; + case N_TYPES.DateTimeN: + if (length === 8) return TYPES.DateTime; + return TYPES.SmallDateTime; + } + return type; +} + +module.exports = getConcreteType; diff --git a/plugins/dbgate-plugin-mssql/src/backend/index.js b/plugins/dbgate-plugin-mssql/src/backend/index.js new file mode 100644 index 000000000..4277c5283 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/index.js @@ -0,0 +1,9 @@ +const driver = require('./driver'); + +module.exports = { + packageName: 'dbgate-plugin-mssql', + driver, + initialize(dbgateEnv) { + driver.initialize(dbgateEnv); + }, +}; diff --git a/plugins/dbgate-plugin-mssql/src/backend/makeUniqueColumnNames.js b/plugins/dbgate-plugin-mssql/src/backend/makeUniqueColumnNames.js new file mode 100644 index 000000000..19587dc3d --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/makeUniqueColumnNames.js @@ -0,0 +1,13 @@ +function makeUniqueColumnNames(res) { + const usedNames = new Set(); + for (let i = 0; i < res.length; i++) { + if (usedNames.has(res[i].columnName)) { + let suffix = 2; + while (usedNames.has(`${res[i].columnName}${suffix}`)) suffix++; + res[i].columnName = `${res[i].columnName}${suffix}`; + } + usedNames.add(res[i].columnName); + } +} + +module.exports = makeUniqueColumnNames; diff --git a/plugins/dbgate-plugin-mssql/src/backend/nativeDriver.js b/plugins/dbgate-plugin-mssql/src/backend/nativeDriver.js new file mode 100644 index 000000000..914c0ccb4 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/nativeDriver.js @@ -0,0 +1,211 @@ +const _ = require('lodash'); +const stream = require('stream'); +const makeUniqueColumnNames = require('./makeUniqueColumnNames'); +let msnodesqlv8; + +// async function nativeQueryCore(pool, sql, options) { +// if (sql == null) { +// return Promise.resolve({ +// rows: [], +// columns: [], +// }); +// } +// return new Promise((resolve, reject) => { +// pool.query(sql, (err, rows) => { +// if (err) reject(err); +// resolve({ +// rows, +// }); +// }); +// }); +// } + +function extractNativeColumns(meta) { + const res = meta.map(col => { + const resCol = { + columnName: col.name, + dataType: col.sqlType.toLowerCase(), + notNull: !col.nullable, + }; + + if (resCol.dataType.endsWith(' identity')) { + resCol.dataType = resCol.dataType.replace(' identity', ''); + resCol.autoIncrement = true; + } + if (col.size && resCol.dataType.includes('char')) { + resCol.dataType += `(${col.size})`; + } + return resCol; + }); + + makeUniqueColumnNames(res); + + return res; +} + +async function nativeConnect({ server, port, user, password, database, authType }) { + let connectionString = `server=${server}`; + if (port && !server.includes('\\')) connectionString += `,${port}`; + connectionString += ';Driver={SQL Server Native Client 11.0}'; + if (authType == 'sspi') connectionString += ';Trusted_Connection=Yes'; + else connectionString += `;UID=${user};PWD=${password}`; + if (database) connectionString += `;Database=${database}`; + return new Promise((resolve, reject) => { + msnodesqlv8.open(connectionString, (err, conn) => { + if (err) reject(err); + conn._connectionType = 'msnodesqlv8'; + resolve(conn); + }); + }); +} + +async function nativeQueryCore(pool, sql, options) { + if (sql == null) { + return Promise.resolve({ + rows: [], + columns: [], + }); + } + return new Promise((resolve, reject) => { + let columns = null; + let currentRow = null; + const q = pool.query(sql); + const rows = []; + + q.on('meta', meta => { + columns = extractNativeColumns(meta); + }); + + q.on('column', (index, data) => { + currentRow[columns[index].columnName] = data; + }); + + q.on('row', index => { + if (currentRow) rows.push(currentRow); + currentRow = {}; + }); + + q.on('error', err => { + reject(err); + }); + + q.on('done', () => { + if (currentRow) rows.push(currentRow); + resolve({ + columns, + rows, + }); + }); + }); +} + +async function nativeReadQuery(pool, sql, structure) { + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + + let columns = null; + let currentRow = null; + const q = pool.query(sql); + + q.on('meta', meta => { + columns = extractNativeColumns(meta); + pass.write({ + __isStreamHeader: true, + ...(structure || { columns }), + }); + }); + + q.on('column', (index, data) => { + currentRow[columns[index].columnName] = data; + }); + + q.on('row', index => { + if (currentRow) pass.write(currentRow); + currentRow = {}; + }); + + q.on('error', err => { + console.error(err); + pass.end(); + }); + + q.on('done', () => { + if (currentRow) pass.write(currentRow); + pass.end(); + }); + + return pass; +} + +async function nativeStream(pool, sql, options) { + const handleInfo = info => { + const { message, lineNumber, procName } = info; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'info', + }); + }; + const handleError = error => { + const { message, lineNumber, procName } = error; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'error', + }); + }; + + let columns = null; + let currentRow = null; + const q = pool.query(sql); + + q.on('meta', meta => { + if (currentRow) options.row(currentRow); + currentRow = null; + columns = extractNativeColumns(meta); + options.recordset(columns); + }); + + q.on('column', (index, data) => { + currentRow[columns[index].columnName] = data; + }); + + q.on('row', index => { + if (currentRow) options.row(currentRow); + currentRow = {}; + }); + + q.on('error', err => { + handleError(err); + options.done(); + }); + + q.on('info', info => { + handleInfo(info); + }); + + q.on('done', () => { + if (currentRow) options.row(currentRow); + options.done(); + }); +} + +const initialize = dbgateEnv => { + if (dbgateEnv.nativeModules && dbgateEnv.nativeModules.msnodesqlv8) { + msnodesqlv8 = dbgateEnv.nativeModules.msnodesqlv8(); + } +}; + +module.exports = { + nativeConnect, + nativeQueryCore, + nativeReadQuery, + nativeStream, + initialize, +}; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/columns.js b/plugins/dbgate-plugin-mssql/src/backend/sql/columns.js new file mode 100644 index 000000000..3de7ceffa --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/columns.js @@ -0,0 +1,20 @@ +module.exports = ` +select c.name as columnName, t.name as dataType, c.object_id as objectId, c.is_identity as isIdentity, + c.max_length as maxLength, c.precision, c.scale, c.is_nullable as isNullable, + col.CHARACTER_MAXIMUM_LENGTH as charMaxLength, + d.definition as defaultValue, d.name as defaultConstraint, + m.definition as computedExpression, m.is_persisted as isPersisted, c.column_id as columnId, + col.NUMERIC_PRECISION as numericPrecision, + col.NUMERIC_SCALE as numericScale, + -- TODO only if version >= 2008 + c.is_sparse as isSparse +from sys.columns c +inner join sys.types t on c.system_type_id = t.system_type_id and c.user_type_id = t.user_type_id +inner join sys.objects o on c.object_id = o.object_id +INNER JOIN sys.schemas u ON u.schema_id=o.schema_id +INNER JOIN INFORMATION_SCHEMA.COLUMNS col ON col.TABLE_NAME = o.name AND col.TABLE_SCHEMA = u.name and col.COLUMN_NAME = c.name +left join sys.default_constraints d on c.default_object_id = d.object_id +left join sys.computed_columns m on m.object_id = c.object_id and m.column_id = c.column_id +where o.type = 'U' and o.object_id =[OBJECT_ID_CONDITION] +order by c.column_id +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/foreignKeys.js b/plugins/dbgate-plugin-mssql/src/backend/sql/foreignKeys.js new file mode 100644 index 000000000..568117b77 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/foreignKeys.js @@ -0,0 +1,40 @@ +module.exports = ` +SELECT + schemaName = FK.TABLE_SCHEMA, + pureName = FK.TABLE_NAME, + columnName = CU.COLUMN_NAME, + + refSchemaName = ISNULL(IXS.name, PK.TABLE_SCHEMA), + refTableName = ISNULL(IXT.name, PK.TABLE_NAME), + refColumnName = IXCC.name, + + constraintName = C.CONSTRAINT_NAME, + updateAction = rc.UPDATE_RULE, + deleteAction = rc.DELETE_RULE, + + objectId = o.object_id + +FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C +INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME + +LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME +LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME +--LEFT JOIN ( +--SELECT i1.TABLE_NAME, i2.COLUMN_NAME +--FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 +--INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME +--WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' +--) PT ON PT.TABLE_NAME = PK.TABLE_NAME +INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc ON FK.CONSTRAINT_NAME = rc.CONSTRAINT_NAME + +LEFT JOIN sys.indexes IX ON IX.name = C.UNIQUE_CONSTRAINT_NAME +LEFT JOIN sys.objects IXT ON IXT.object_id = IX.object_id +LEFT JOIN sys.index_columns IXC ON IX.index_id = IXC.index_id and IX.object_id = IXC.object_id +LEFT JOIN sys.columns IXCC ON IXCC.column_id = IXC.column_id AND IXCC.object_id = IXC.object_id +LEFT JOIN sys.schemas IXS ON IXT.schema_id = IXS.schema_id + +inner join sys.objects o on FK.TABLE_NAME = o.name +inner join sys.schemas s on o.schema_id = s.schema_id and FK.TABLE_SCHEMA = s.name + +where o.object_id =[OBJECT_ID_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/getSchemas.js b/plugins/dbgate-plugin-mssql/src/backend/sql/getSchemas.js new file mode 100644 index 000000000..a7cb0a4de --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/getSchemas.js @@ -0,0 +1 @@ +module.exports = `select schema_id as objectId, name as schemaName from sys.schemas`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/index.js b/plugins/dbgate-plugin-mssql/src/backend/sql/index.js new file mode 100644 index 000000000..cb40e88a5 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/index.js @@ -0,0 +1,23 @@ +const columns = require('./columns'); +const foreignKeys = require('./foreignKeys'); +const primaryKeys = require('./primaryKeys'); +const tables = require('./tables'); +const modifications = require('./modifications'); +const loadSqlCode = require('./loadSqlCode'); +const views = require('./views'); +const programmables = require('./programmables'); +const viewColumns = require('./viewColumns'); +const getSchemas = require('./getSchemas'); + +module.exports = { + columns, + tables, + foreignKeys, + primaryKeys, + modifications, + loadSqlCode, + views, + programmables, + viewColumns, + getSchemas, +}; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/loadSqlCode.js b/plugins/dbgate-plugin-mssql/src/backend/sql/loadSqlCode.js new file mode 100644 index 000000000..c5f8ee87d --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/loadSqlCode.js @@ -0,0 +1,8 @@ +module.exports = ` +select s.name as pureName, u.name as schemaName, c.text AS codeText + from sys.objects s + inner join sys.syscomments c on s.object_id = c.id + inner join sys.schemas u on u.schema_id = s.schema_id +where (s.object_id =[OBJECT_ID_CONDITION]) +order by u.name, s.name, c.colid +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/modifications.js b/plugins/dbgate-plugin-mssql/src/backend/sql/modifications.js new file mode 100644 index 000000000..4389fd6ca --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/modifications.js @@ -0,0 +1,6 @@ +module.exports = ` +select o.object_id as objectId, o.modify_date as modifyDate, o.type, o.name as pureName, s.name as schemaName +from sys.objects o +inner join sys.schemas s on o.schema_id = s.schema_id +where o.type in ('U', 'V', 'P', 'IF', 'FN', 'TF') -- , 'TR' - triggers disabled +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/primaryKeys.js b/plugins/dbgate-plugin-mssql/src/backend/sql/primaryKeys.js new file mode 100644 index 000000000..f67beb7f2 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/primaryKeys.js @@ -0,0 +1,14 @@ +module.exports = ` +select o.object_id, pureName = t.Table_Name, schemaName = t.Table_Schema, columnName = c.Column_Name, constraintName=t.constraint_name from + INFORMATION_SCHEMA.TABLE_CONSTRAINTS t, + sys.objects o, + sys.schemas s, + INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE c +where + c.Constraint_Name = t.Constraint_Name + and t.table_name = o.name + and o.schema_id = s.schema_id and t.Table_Schema = s.name + and c.Table_Name = t.Table_Name + and Constraint_Type = 'PRIMARY KEY' + and o.object_id =[OBJECT_ID_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/programmables.js b/plugins/dbgate-plugin-mssql/src/backend/sql/programmables.js new file mode 100644 index 000000000..8e651d3ec --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/programmables.js @@ -0,0 +1,6 @@ +module.exports = ` +select o.name as pureName, s.name as schemaName, o.object_id as objectId, o.create_date as createDate, o.modify_date as modifyDate, o.type as sqlObjectType +from sys.objects o +inner join sys.schemas s on o.schema_id = s.schema_id +where o.type in ('P', 'IF', 'FN', 'TF') and o.object_id =[OBJECT_ID_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js b/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js new file mode 100644 index 000000000..46ab78440 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js @@ -0,0 +1,8 @@ +module.exports = ` +select + o.name as pureName, s.name as schemaName, o.object_id as objectId, + o.create_date as createDate, o.modify_date as modifyDate +from sys.tables o +inner join sys.schemas s on o.schema_id = s.schema_id +where o.object_id =[OBJECT_ID_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/viewColumns.js b/plugins/dbgate-plugin-mssql/src/backend/sql/viewColumns.js new file mode 100644 index 000000000..0868c43ae --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/viewColumns.js @@ -0,0 +1,18 @@ +module.exports = ` +select + o.object_id AS objectId, + col.TABLE_SCHEMA as schemaName, + col.TABLE_NAME as pureName, + col.COLUMN_NAME as columnName, + col.IS_NULLABLE as isNullable, + col.DATA_TYPE as dataType, + col.CHARACTER_MAXIMUM_LENGTH as charMaxLength, + col.NUMERIC_PRECISION as precision, + col.NUMERIC_SCALE as scale, + col.COLUMN_DEFAULT +FROM sys.objects o +INNER JOIN sys.schemas u ON u.schema_id=o.schema_id +INNER JOIN INFORMATION_SCHEMA.COLUMNS col ON col.TABLE_NAME = o.name AND col.TABLE_SCHEMA = u.name +WHERE o.type in ('V') and o.object_id =[OBJECT_ID_CONDITION] +order by col.ORDINAL_POSITION +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/views.js b/plugins/dbgate-plugin-mssql/src/backend/sql/views.js new file mode 100644 index 000000000..f0edb498f --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/views.js @@ -0,0 +1,10 @@ +module.exports = ` +SELECT + o.name as pureName, + u.name as schemaName, + o.object_id as objectId, + o.create_date as createDate, + o.modify_date as modifyDate +FROM sys.objects o INNER JOIN sys.schemas u ON u.schema_id=o.schema_id +WHERE type in ('V') and o.object_id =[OBJECT_ID_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js new file mode 100644 index 000000000..2964642b4 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js @@ -0,0 +1,180 @@ +const _ = require('lodash'); +const stream = require('stream'); +const tedious = require('tedious'); +const makeUniqueColumnNames = require('./makeUniqueColumnNames'); + +function extractTediousColumns(columns, addDriverNativeColumn = false) { + const res = columns.map(col => { + const resCol = { + columnName: col.colName, + dataType: col.type.name.toLowerCase(), + driverNativeColumn: addDriverNativeColumn ? col : undefined, + + notNull: !(col.flags & 0x01), + autoIncrement: !!(col.flags & 0x10), + }; + if (col.dataLength) resCol.dataType += `(${col.dataLength})`; + return resCol; + }); + + makeUniqueColumnNames(res); + + return res; +} + +async function tediousConnect({ server, port, user, password, database, ssl }) { + return new Promise((resolve, reject) => { + const connection = new tedious.Connection({ + server, + + authentication: { + type: 'default', + options: { + userName: user, + password: password, + }, + }, + + options: { + encrypt: !!ssl, + cryptoCredentialsDetails: ssl ? _.pick(ssl, ['ca', 'cert', 'key']) : undefined, + trustServerCertificate: ssl ? (!ssl.ca && !ssl.cert && !ssl.key ? true : ssl.rejectUnauthorized) : undefined, + enableArithAbort: true, + validateBulkLoadParameters: false, + requestTimeout: 1000 * 3600, + database, + port: port ? parseInt(port) : undefined, + }, + }); + connection.on('connect', function (err) { + if (err) { + reject(err); + } + connection._connectionType = 'tedious'; + resolve(connection); + }); + connection.connect(); + }); +} + +async function tediousQueryCore(pool, sql, options) { + if (sql == null) { + return Promise.resolve({ + rows: [], + columns: [], + }); + } + const { addDriverNativeColumn } = options || {}; + return new Promise((resolve, reject) => { + const result = { + rows: [], + columns: [], + }; + const request = new tedious.Request(sql, (err, rowCount) => { + if (err) reject(err); + else resolve(result); + }); + request.on('columnMetadata', function (columns) { + result.columns = extractTediousColumns(columns, addDriverNativeColumn); + }); + request.on('row', function (columns) { + result.rows.push( + _.zipObject( + result.columns.map(x => x.columnName), + columns.map(x => x.value) + ) + ); + }); + pool.execSql(request); + }); +} + +async function tediousReadQuery(pool, sql, structure) { + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + let currentColumns = []; + + const request = new tedious.Request(sql, (err, rowCount) => { + if (err) console.error(err); + pass.end(); + }); + request.on('columnMetadata', function (columns) { + currentColumns = extractTediousColumns(columns); + pass.write({ + __isStreamHeader: true, + ...(structure || { columns: currentColumns }), + }); + }); + request.on('row', function (columns) { + const row = _.zipObject( + currentColumns.map(x => x.columnName), + columns.map(x => x.value) + ); + pass.write(row); + }); + pool.execSql(request); + + return pass; +} + +async function tediousStream(pool, sql, options) { + let currentColumns = []; + + const handleInfo = info => { + const { message, lineNumber, procName } = info; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'info', + }); + }; + const handleError = error => { + const { message, lineNumber, procName } = error; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'error', + }); + }; + + pool.on('infoMessage', handleInfo); + pool.on('errorMessage', handleError); + const request = new tedious.Request(sql, (err, rowCount) => { + // if (err) reject(err); + // else resolve(result); + options.done(); + pool.off('infoMessage', handleInfo); + pool.off('errorMessage', handleError); + + options.info({ + message: `${rowCount} rows affected`, + time: new Date(), + severity: 'info', + }); + }); + request.on('columnMetadata', function (columns) { + currentColumns = extractTediousColumns(columns); + options.recordset(currentColumns); + }); + request.on('row', function (columns) { + const row = _.zipObject( + currentColumns.map(x => x.columnName), + columns.map(x => x.value) + ); + options.row(row); + }); + pool.execSqlBatch(request); +} + +module.exports = { + tediousConnect, + tediousQueryCore, + tediousReadQuery, + tediousStream, +}; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js new file mode 100644 index 000000000..8b6459b82 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -0,0 +1,113 @@ +const { SqlDumper } = require('dbgate-tools'); + +class MsSqlDumper extends SqlDumper { + autoIncrement() { + this.put(' ^identity'); + } + + putStringValue(value) { + if (/[^\u0000-\u00ff]/.test(value)) { + this.putRaw('N'); + } + super.putStringValue(value); + } + + allowIdentityInsert(table, allow) { + this.putCmd('^set ^identity_insert %f %k', table, allow ? 'on' : 'off'); + } + + /** @param type {import('dbgate-types').TransformType} */ + transform(type, dumpExpr) { + switch (type) { + case 'GROUP:YEAR': + case 'YEAR': + this.put('^datepart(^year, %c)', dumpExpr); + break; + case 'MONTH': + this.put('^datepart(^month, %c)', dumpExpr); + break; + case 'DAY': + this.put('^datepart(^day, %c)', dumpExpr); + break; + case 'GROUP:MONTH': + this.put( + "^convert(^varchar(100), ^datepart(^year, %c)) + '-' + right('0' + ^convert(^varchar(100), ^datepart(^month, %c)), 2)", + dumpExpr, + dumpExpr + ); + break; + case 'GROUP:DAY': + this.put( + "^^convert(^varchar(100), ^datepart(^year, %c)) + '-' + ^right('0' + ^convert(^varchar(100), ^datepart(^month, %c)), 2)+'-' + ^right('0' + ^convert(^varchar(100), ^datepart(^day, %c)), 2)", + dumpExpr, + dumpExpr, + dumpExpr + ); + break; + default: + dumpExpr(); + break; + } + } + + renameObject(obj, newname) { + this.putCmd("^execute sp_rename '%f', '%s', 'OBJECT'", obj, newname); + } + + changeObjectSchema(obj, newschema) { + this.putCmd("^execute sp_changeobjectowner '%f', '%s'", obj, newschema); + } + + dropTable(obj, options = {}) { + if (options.testIfExists) { + this.put("IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'%f') AND type in (N'U'))&n", obj); + } + super.dropTable(obj, options); + } + + dropDefault(col) { + if (col.defaultConstraint) { + this.putCmd("^alter ^table %f ^drop ^constraint %i", col, col.defaultConstraint); + } + } + + guessDefaultName(col) { + return col.defaultConstraint || `DF${col.schemaName || 'dbo'}_${col.pureName}_col.columnName` + } + + createDefault(col) { + if (!col.defaultValue) return; + const defsql = col.defaultValue; + if (!defsql) { + const defname = this.guessDefaultName(col); + this.putCmd("^alter ^table %f ^add ^constraint %i ^default %s for %i", col, defname, defsql, col.columnName); + } + } + + renameColumn(column, newcol) { + this.putCmd("^execute sp_rename '%f.%i', '%s', 'COLUMN'", column, column.columnName, newcol); + } + + renameConstraint(cnt, newname) { + if (cnt.constraintType == 'index') this.putCmd("^execute sp_rename '%f.%i', '%s', 'INDEX'", cnt, cnt.constraintName, newname); + else this.putCmd("^execute sp_rename '%f', '%s', 'OBJECT'", { schemaName: cnt.schemaName, pureName: cnt.constraintName }, newname); + } +} + +MsSqlDumper.prototype.renameView = MsSqlDumper.prototype.renameObject; +MsSqlDumper.prototype.changeViewSchema = MsSqlDumper.prototype.changeObjectSchema; + +MsSqlDumper.prototype.renameProcedure = MsSqlDumper.prototype.renameObject; +MsSqlDumper.prototype.changeProcedureSchema = MsSqlDumper.prototype.changeObjectSchema; + +MsSqlDumper.prototype.renameFunction = MsSqlDumper.prototype.renameObject; +MsSqlDumper.prototype.changeFunctionSchema = MsSqlDumper.prototype.changeObjectSchema; + +MsSqlDumper.prototype.renameTrigger = MsSqlDumper.prototype.renameObject; +MsSqlDumper.prototype.changeTriggerSchema = MsSqlDumper.prototype.changeObjectSchema; + +MsSqlDumper.prototype.renameTable = MsSqlDumper.prototype.renameObject; +MsSqlDumper.prototype.changeTableSchema = MsSqlDumper.prototype.changeObjectSchema; + + +module.exports = MsSqlDumper; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js new file mode 100644 index 000000000..0c20c5745 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -0,0 +1,29 @@ +const { driverBase } = require('dbgate-tools'); +const MsSqlDumper = require('./MsSqlDumper'); + +/** @type {import('dbgate-types').SqlDialect} */ +const dialect = { + limitSelect: true, + rangeSelect: true, + offsetFetchRangeSyntax: true, + stringEscapeChar: "'", + fallbackDataType: 'nvarchar(max)', + explicitDropConstraint: false, + enableConstraintsPerTable: true, + anonymousPrimaryKey: false, + quoteIdentifier(s) { + return `[${s}]`; + }, +}; + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + dumperClass: MsSqlDumper, + dialect, + engine: 'mssql@dbgate-plugin-mssql', + title: 'Microsoft SQL Server', + defaultPort: 1433, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/index.js b/plugins/dbgate-plugin-mssql/src/frontend/index.js new file mode 100644 index 000000000..841ed051a --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/frontend/index.js @@ -0,0 +1,5 @@ +import driver from './driver'; + +export default { + driver, +}; diff --git a/plugins/dbgate-plugin-mssql/webpack-backend.config.js b/plugins/dbgate-plugin-mssql/webpack-backend.config.js new file mode 100644 index 000000000..c00656b5f --- /dev/null +++ b/plugins/dbgate-plugin-mssql/webpack-backend.config.js @@ -0,0 +1,22 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mssql/webpack-frontend.config.js b/plugins/dbgate-plugin-mssql/webpack-frontend.config.js new file mode 100644 index 000000000..2703ab7e6 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/webpack-frontend.config.js @@ -0,0 +1,23 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mysql/LICENSE b/plugins/dbgate-plugin-mysql/LICENSE new file mode 100644 index 000000000..c15ede038 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jan Prochazka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/dbgate-plugin-mysql/README.md b/plugins/dbgate-plugin-mysql/README.md new file mode 100644 index 000000000..b3e4fc850 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/README.md @@ -0,0 +1,6 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-mysql.svg)](https://www.npmjs.com/package/dbgate-plugin-mysql) + +# dbgate-plugin-mysql + +Use DbGate for install of this plugin diff --git a/plugins/dbgate-plugin-mysql/icon.svg b/plugins/dbgate-plugin-mysql/icon.svg new file mode 100644 index 000000000..a31893f5e --- /dev/null +++ b/plugins/dbgate-plugin-mysql/icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + diff --git a/plugins/dbgate-plugin-mysql/package.json b/plugins/dbgate-plugin-mysql/package.json new file mode 100644 index 000000000..e5ecb59bf --- /dev/null +++ b/plugins/dbgate-plugin-mysql/package.json @@ -0,0 +1,39 @@ +{ + "name": "dbgate-plugin-mysql", + "main": "dist/backend.js", + "version": "1.2.2", + "homepage": "https://github.com/dbgate/dbgate-plugin-mysql", + "description": "MySQL connect plugin for DbGate", + "repository": { + "type": "git", + "url": "https://github.com/dbgate/dbgate-plugin-mysql.git" + }, + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "author": "Jan Prochazka", + "license": "MIT", + "keywords": [ + "sql", + "dbgate", + "dbgateplugin", + "mysql" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-mysql", + "plugout": "dbgate-plugout dbgate-plugin-mysql", + "prepublishOnly": "yarn build" + }, + "devDependencies": { + "@verycrazydog/mysql-parser": "^1.2.0", + "dbgate-plugin-tools": "^1.0.4", + "dbgate-tools": "^4.0.3-rc.1", + "mysql2": "^2.2.5", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + } +} diff --git a/plugins/dbgate-plugin-mysql/prettier.config.js b/plugins/dbgate-plugin-mysql/prettier.config.js new file mode 100644 index 000000000..c05d71875 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/prettier.config.js @@ -0,0 +1,9 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + arrowParens: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-mysql/src/backend/Analyser.js b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js new file mode 100644 index 000000000..9e1f043bb --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/Analyser.js @@ -0,0 +1,222 @@ +const fp = require('lodash/fp'); +const _ = require('lodash'); +const sql = require('./sql'); + +const { DatabaseAnalyser } = require('dbgate-tools'); +const { isTypeString, isTypeNumeric } = require('dbgate-tools'); +const { rangeStep } = require('lodash/fp'); + +function getColumnInfo({ + isNullable, + extra, + columnName, + dataType, + charMaxLength, + numericPrecision, + numericScale, + defaultValue, +}) { + let fullDataType = dataType; + if (charMaxLength && isTypeString(dataType)) fullDataType = `${dataType}(${charMaxLength})`; + if (numericPrecision && numericScale && isTypeNumeric(dataType)) + fullDataType = `${dataType}(${numericPrecision},${numericScale})`; + return { + notNull: !isNullable || isNullable == 'NO' || isNullable == 'no', + autoIncrement: extra && extra.toLowerCase().includes('auto_increment'), + columnName, + dataType: fullDataType, + defaultValue, + }; +} + +function objectTypeToField(type) { + if (type == 'VIEW') return 'views'; + if (type == 'BASE TABLE') return 'tables'; + return null; +} + +class Analyser extends DatabaseAnalyser { + constructor(pool, driver) { + super(pool, driver); + } + + createQuery(resFileName, typeFields) { + let res = sql[resFileName]; + if (this.singleObjectFilter) { + const { typeField, pureName } = this.singleObjectFilter; + if (!typeFields || !typeFields.includes(typeField)) return null; + res = res.replace('=[OBJECT_NAME_CONDITION]', ` = '${pureName}'`).replace('#DATABASE#', this.pool._database_name); + return res; + } + if (!this.modifications || !typeFields || this.modifications.length == 0) { + res = res.replace('=[OBJECT_NAME_CONDITION]', ' is not null'); + } else { + const filterNames = this.modifications + .filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change')) + .map(x => x.newName && x.newName.pureName) + .filter(Boolean); + if (filterNames.length == 0) { + res = res.replace('=[OBJECT_NAME_CONDITION]', ' IS NULL'); + } else { + res = res.replace('=[OBJECT_NAME_CONDITION]', ` in (${filterNames.map(x => `'${x}'`).join(',')})`); + } + } + res = res.replace('#DATABASE#', this.pool._database_name); + return res; + } + + getRequestedViewNames(allViewNames) { + if (this.singleObjectFilter) { + const { typeField, pureName } = this.singleObjectFilter; + if (typeField == 'views') return [pureName]; + } + if (this.modifications) { + return this.modifications.filter(x => x.objectTypeField == 'views').map(x => x.newName.pureName); + } + return allViewNames; + } + + async getViewTexts(allViewNames) { + const res = {}; + for (const viewName of this.getRequestedViewNames(allViewNames)) { + const resp = await this.driver.query(this.pool, `SHOW CREATE VIEW \`${viewName}\``); + res[viewName] = resp.rows[0]['Create View']; + } + return res; + } + + async _runAnalysis() { + const tables = await this.driver.query(this.pool, this.createQuery('tables', ['tables'])); + const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables', 'views'])); + const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); + const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); + const views = await this.driver.query(this.pool, this.createQuery('views', ['views'])); + const programmables = await this.driver.query( + this.pool, + this.createQuery('programmables', ['procedures', 'functions']) + ); + + const viewTexts = await this.getViewTexts(views.rows.map(x => x.pureName)); + + return this.mergeAnalyseResult({ + tables: tables.rows.map(table => ({ + ...table, + objectId: table.pureName, + columns: columns.rows.filter(col => col.pureName == table.pureName).map(getColumnInfo), + primaryKey: DatabaseAnalyser.extractPrimaryKeys(table, pkColumns.rows), + foreignKeys: DatabaseAnalyser.extractForeignKeys(table, fkColumns.rows), + })), + views: views.rows.map(view => ({ + ...view, + objectId: view.pureName, + columns: columns.rows.filter(col => col.pureName == view.pureName).map(getColumnInfo), + createSql: viewTexts[view.pureName], + requiresFormat: true, + })), + procedures: programmables.rows + .filter(x => x.objectType == 'PROCEDURE') + .map(fp.omit(['objectType'])) + .map(x => ({ ...x, objectId: x.pureName })), + functions: programmables.rows + .filter(x => x.objectType == 'FUNCTION') + .map(fp.omit(['objectType'])) + .map(x => ({ ...x, objectId: x.pureName })), + }); + } + + getDeletedObjectsForField(nameArray, objectTypeField) { + return this.structure[objectTypeField] + .filter(x => !nameArray.includes(x.pureName)) + .map(x => ({ + oldName: _.pick(x, ['pureName']), + action: 'remove', + objectTypeField, + objectId: x.pureName, + })); + } + + getDeletedObjects(nameArray) { + return [ + ...this.getDeletedObjectsForField(nameArray, 'tables'), + ...this.getDeletedObjectsForField(nameArray, 'views'), + ...this.getDeletedObjectsForField(nameArray, 'procedures'), + ...this.getDeletedObjectsForField(nameArray, 'functions'), + ...this.getDeletedObjectsForField(nameArray, 'triggers'), + ]; + } + + async getModifications() { + const tableModificationsQueryData = await this.driver.query(this.pool, this.createQuery('tableModifications')); + const procedureModificationsQueryData = await this.driver.query( + this.pool, + this.createQuery('procedureModifications') + ); + const functionModificationsQueryData = await this.driver.query( + this.pool, + this.createQuery('functionModifications') + ); + + const allModifications = _.compact([ + ...tableModificationsQueryData.rows.map(x => { + if (x.objectType == 'BASE TABLE') return { ...x, objectTypeField: 'tables' }; + if (x.objectType == 'VIEW') return { ...x, objectTypeField: 'views' }; + return null; + }), + ...procedureModificationsQueryData.rows.map(x => ({ + objectTypeField: 'procedures', + modifyDate: x.Modified, + pureName: x.Name, + })), + ...functionModificationsQueryData.rows.map(x => ({ + objectTypeField: 'functions', + modifyDate: x.Modified, + pureName: x.Name, + })), + ]); + + // console.log('allModifications', allModifications); + // console.log( + // 'DATES', + // this.structure.procedures.map((x) => x.modifyDate) + // ); + // console.log('MOD - SRC', modifications); + // console.log( + // 'MODs', + // this.structure.tables.map((x) => x.modifyDate) + // ); + const modifications = allModifications.map(x => { + const { objectType, modifyDate, pureName } = x; + const field = objectTypeToField(objectType); + + if (!field || !this.structure[field]) return null; + // @ts-ignore + const obj = this.structure[field].find(x => x.pureName == pureName); + + // object not modified + if (obj && Math.abs(new Date(modifyDate).getTime() - new Date(obj.modifyDate).getTime()) < 1000) return null; + + // console.log('MODIFICATION OF ', field, pureName, modifyDate, obj.modifyDate); + + /** @type {import('dbgate-types').DatabaseModification} */ + const action = obj + ? { + newName: { pureName }, + oldName: _.pick(obj, ['pureName']), + action: 'change', + objectTypeField: field, + objectId: pureName, + } + : { + newName: { pureName }, + action: 'add', + objectTypeField: field, + objectId: pureName, + }; + return action; + }); + + return [..._.compact(modifications), ...this.getDeletedObjects([...allModifications.map(x => x.pureName)])]; + } +} + +module.exports = Analyser; diff --git a/plugins/dbgate-plugin-mysql/src/backend/driver.js b/plugins/dbgate-plugin-mysql/src/backend/driver.js new file mode 100644 index 000000000..3a3120836 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/driver.js @@ -0,0 +1,186 @@ +const _ = require('lodash'); +const stream = require('stream'); +const driverBase = require('../frontend/driver'); +const Analyser = require('./Analyser'); +const mysql2 = require('mysql2'); +const { createBulkInsertStreamBase, makeUniqueColumnNames } = require('dbgate-tools'); +const mysqlSplitter = require('@verycrazydog/mysql-parser'); + +function extractColumns(fields) { + if (fields) { + const res = fields.map(col => ({ + columnName: col.name, + })); + makeUniqueColumnNames(res); + return res; + } + return null; +} + +function zipDataRow(rowArray, columns) { + return _.zipObject( + columns.map(x => x.columnName), + rowArray + ); +} + +async function runQueryItem(connection, sql) { + return new Promise((resolve, reject) => { + connection.query(sql, function (error, results, fields) { + if (error) reject(error); + const columns = extractColumns(fields); + resolve({ rows: results && columns && results.map && results.map(row => zipDataRow(row, columns)), columns }); + }); + }); +} + +async function runStreamItem(connection, sql, options) { + return new Promise((resolve, reject) => { + const query = connection.query(sql); + let columns = []; + + // const handleInfo = (info) => { + // const { message, lineNumber, procName } = info; + // options.info({ + // message, + // line: lineNumber, + // procedure: procName, + // time: new Date(), + // severity: 'info', + // }); + // }; + + const handleEnd = () => { + resolve(); + }; + + const handleRow = row => { + if (row && row.constructor && row.constructor.name == 'OkPacket') { + options.info({ + message: `${row.affectedRows} rows affected`, + time: new Date(), + severity: 'info', + }); + } else { + if (columns) { + options.row(zipDataRow(row, columns)); + } + } + }; + + const handleFields = fields => { + columns = extractColumns(fields); + if (columns) options.recordset(columns); + }; + + const handleError = error => { + console.log('ERROR', error); + const { message, lineNumber, procName } = error; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'error', + }); + }; + + query.on('error', handleError).on('fields', handleFields).on('result', handleRow).on('end', handleEnd); + }); +} + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + analyserClass: Analyser, + + async connect({ server, port, user, password, database, ssl }) { + const connection = mysql2.createConnection({ + host: server, + port, + user, + password, + database, + ssl, + rowsAsArray: true, + supportBigNumbers: true, + bigNumberStrings: true, + // TODO: test following options + // multipleStatements: true, + // dateStrings: true, + }); + connection._database_name = database; + return connection; + }, + async query(connection, sql) { + if (sql == null) { + return { + rows: [], + columns: [], + }; + } + + const sqlSplitted = mysqlSplitter.split(sql); + let res = { + rows: [], + columns: [], + }; + for (const sqlItem of sqlSplitted) { + const resultItem = await runQueryItem(connection, sqlItem); + if (resultItem.rows && resultItem.columns && resultItem.columns.length > 0) { + res = resultItem; + } + } + return res; + }, + async stream(connection, sql, options) { + const sqlSplitted = mysqlSplitter.split(sql); + + for (const sqlItem of sqlSplitted) { + await runStreamItem(connection, sqlItem, options); + } + + options.done(); + }, + async readQuery(connection, sql, structure) { + const query = connection.query(sql); + + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + + let columns = []; + query + .on('error', err => { + console.error(err); + pass.end(); + }) + .on('fields', fields => { + columns = extractColumns(fields); + pass.write({ + __isStreamHeader: true, + ...(structure || { columns }), + }); + }) + .on('result', row => pass.write(zipDataRow(row, columns))) + .on('end', () => pass.end()); + + return pass; + }, + async getVersion(connection) { + const { rows } = await this.query(connection, "show variables like 'version'"); + const version = rows[0].Value; + return { version }; + }, + async listDatabases(connection) { + const { rows } = await this.query(connection, 'show databases'); + return rows.map(x => ({ name: x.Database })); + }, + async writeTable(pool, name, options) { + // @ts-ignore + return createBulkInsertStreamBase(this, stream, pool, name, options); + }, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mysql/src/backend/index.js b/plugins/dbgate-plugin-mysql/src/backend/index.js new file mode 100644 index 000000000..ef1600fd9 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/index.js @@ -0,0 +1,6 @@ +const driver = require('./driver'); + +module.exports = { + packageName: 'dbgate-plugin-mysql', + driver, +}; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/columns.js b/plugins/dbgate-plugin-mysql/src/backend/sql/columns.js new file mode 100644 index 000000000..0adb27e2b --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/columns.js @@ -0,0 +1,15 @@ +module.exports = ` +select + TABLE_NAME as pureName, + COLUMN_NAME as columnName, + IS_NULLABLE as isNullable, + DATA_TYPE as dataType, + CHARACTER_MAXIMUM_LENGTH as charMaxLength, + NUMERIC_PRECISION as numericPrecision, + NUMERIC_SCALE as numericScale, + COLUMN_DEFAULT as defaultValue, + EXTRA as extra +from INFORMATION_SCHEMA.COLUMNS +where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_NAME_CONDITION] +order by ORDINAL_POSITION +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/foreignKeys.js b/plugins/dbgate-plugin-mysql/src/backend/sql/foreignKeys.js new file mode 100644 index 000000000..5b43b4031 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/foreignKeys.js @@ -0,0 +1,17 @@ +module.exports = ` +select + REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME as constraintName, + REFERENTIAL_CONSTRAINTS.TABLE_NAME as pureName, + REFERENTIAL_CONSTRAINTS.UPDATE_RULE as updateAction, + REFERENTIAL_CONSTRAINTS.DELETE_RULE as deleteAction, + REFERENTIAL_CONSTRAINTS.REFERENCED_TABLE_NAME as refTableName, + KEY_COLUMN_USAGE.COLUMN_NAME as columnName, + KEY_COLUMN_USAGE.REFERENCED_COLUMN_NAME as refColumnName +from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS +inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE + on REFERENTIAL_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME + and REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME + and REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA +where REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and REFERENTIAL_CONSTRAINTS.TABLE_NAME =[OBJECT_NAME_CONDITION] +order by KEY_COLUMN_USAGE.ORDINAL_POSITION +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/functionModifications.js b/plugins/dbgate-plugin-mysql/src/backend/sql/functionModifications.js new file mode 100644 index 000000000..f422a219a --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/functionModifications.js @@ -0,0 +1,3 @@ +module.exports = ` +SHOW FUNCTION STATUS WHERE Db = '#DATABASE#' +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/index.js b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js new file mode 100644 index 000000000..f5a4992ec --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/index.js @@ -0,0 +1,21 @@ +const columns = require('./columns'); +const tables = require('./tables'); +const primaryKeys = require('./primaryKeys'); +const foreignKeys = require('./foreignKeys'); +const tableModifications = require('./tableModifications'); +const views = require('./views'); +const programmables = require('./programmables'); +const procedureModifications = require('./procedureModifications'); +const functionModifications = require('./functionModifications'); + +module.exports = { + columns, + tables, + primaryKeys, + foreignKeys, + tableModifications, + views, + programmables, + procedureModifications, + functionModifications, +}; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/primaryKeys.js b/plugins/dbgate-plugin-mysql/src/backend/sql/primaryKeys.js new file mode 100644 index 000000000..2b1b346e8 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/primaryKeys.js @@ -0,0 +1,12 @@ +module.exports = `select + TABLE_CONSTRAINTS.CONSTRAINT_NAME as constraintName, + TABLE_CONSTRAINTS.TABLE_NAME as pureName, + KEY_COLUMN_USAGE.COLUMN_NAME as columnName +from INFORMATION_SCHEMA.TABLE_CONSTRAINTS +inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE + on TABLE_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME + and TABLE_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME + and TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA +where TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and TABLE_CONSTRAINTS.TABLE_NAME =[OBJECT_NAME_CONDITION] AND TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY' +order by KEY_COLUMN_USAGE.ORDINAL_POSITION +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/procedureModifications.js b/plugins/dbgate-plugin-mysql/src/backend/sql/procedureModifications.js new file mode 100644 index 000000000..0ccbd49f8 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/procedureModifications.js @@ -0,0 +1,3 @@ +module.exports = ` +SHOW PROCEDURE STATUS WHERE Db = '#DATABASE#' +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/programmables.js b/plugins/dbgate-plugin-mysql/src/backend/sql/programmables.js new file mode 100644 index 000000000..dd1b36021 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/programmables.js @@ -0,0 +1,9 @@ +module.exports = ` +select + ROUTINE_NAME as pureName, + ROUTINE_TYPE as objectType, + COALESCE(LAST_ALTERED, CREATED) as modifyDate, + ROUTINE_DEFINITION as createSql +from information_schema.routines +where ROUTINE_SCHEMA = '#DATABASE#' and ROUTINE_NAME =[OBJECT_NAME_CONDITION] +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/tableModifications.js b/plugins/dbgate-plugin-mysql/src/backend/sql/tableModifications.js new file mode 100644 index 000000000..6382ea5ae --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/tableModifications.js @@ -0,0 +1,8 @@ +module.exports = ` +select + TABLE_NAME as pureName, + TABLE_TYPE as objectType, + case when ENGINE='InnoDB' then CREATE_TIME else coalesce(UPDATE_TIME, CREATE_TIME) end as modifyDate +from information_schema.tables +where TABLE_SCHEMA = '#DATABASE#' +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/tables.js b/plugins/dbgate-plugin-mysql/src/backend/sql/tables.js new file mode 100644 index 000000000..eed928431 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/tables.js @@ -0,0 +1,7 @@ +module.exports = ` +select + TABLE_NAME as pureName, + case when ENGINE='InnoDB' then CREATE_TIME else coalesce(UPDATE_TIME, CREATE_TIME) end as modifyDate +from information_schema.tables +where TABLE_SCHEMA = '#DATABASE#' and TABLE_TYPE='BASE TABLE' and TABLE_NAME =[OBJECT_NAME_CONDITION]; +`; diff --git a/plugins/dbgate-plugin-mysql/src/backend/sql/views.js b/plugins/dbgate-plugin-mysql/src/backend/sql/views.js new file mode 100644 index 000000000..ea4d8ef35 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/backend/sql/views.js @@ -0,0 +1,7 @@ +module.exports = ` +select + TABLE_NAME as pureName, + coalesce(UPDATE_TIME, CREATE_TIME) as modifyDate +from information_schema.tables +where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_NAME_CONDITION] and TABLE_TYPE = 'VIEW'; +`; diff --git a/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js b/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js new file mode 100644 index 000000000..993cf8249 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js @@ -0,0 +1,68 @@ +const { SqlDumper } = require('dbgate-tools'); + +class Dumper extends SqlDumper { + /** @param type {import('dbgate-types').TransformType} */ + transform(type, dumpExpr) { + switch (type) { + case 'GROUP:YEAR': + case 'YEAR': + this.put('^year(%c)', dumpExpr); + break; + case 'MONTH': + this.put('^month(%c)', dumpExpr); + break; + case 'DAY': + this.put('^day(%c)', dumpExpr); + break; + case 'GROUP:MONTH': + this.put("^date_format(%c, '%s')", dumpExpr, '%Y-%m'); + break; + case 'GROUP:DAY': + this.put("^date_format(%c, '%s')", dumpExpr, '%Y-%m-%d'); + break; + default: + dumpExpr(); + break; + } + } + + renameTable(obj, newName) { + this.putCmd('^rename ^table %f ^to %i', obj, newName); + } + + changeColumn(oldcol, newcol, constraints) { + this.put('^alter ^table %f ^change ^column %i %i ', oldcol, oldcol.columnName, newcol.columnName); + this.columnDefinition(newcol, true, true, true); + this.inlineConstraints(constraints); + this.endCommand(); + } + + renameColumn(column, newcol) { + this.changeColumn( + column, + { + ...column, + columnName: newcol, + }, + [] + ); + } + + enableConstraints(table, enabled) { + this.putCmd('^set FOREIGN_KEY_CHECKS = %s', enabled ? '1' : '0'); + } + + comment(value) { + this.put('/* %s */', value); + } + + beginTransaction() { + this.putCmd('^start ^transaction'); + } + + selectTableIntoNewTable(sourceName, targetName) { + this.putCmd('^create ^table %f (^select * ^from %f)', targetName, sourceName); + } +} + +module.exports = Dumper; diff --git a/plugins/dbgate-plugin-mysql/src/frontend/driver.js b/plugins/dbgate-plugin-mysql/src/frontend/driver.js new file mode 100644 index 000000000..93465e2d6 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/frontend/driver.js @@ -0,0 +1,27 @@ +const { driverBase } = require('dbgate-tools'); +const Dumper = require('./Dumper'); + +/** @type {import('dbgate-types').SqlDialect} */ +const dialect = { + rangeSelect: true, + stringEscapeChar: '\\', + fallbackDataType: 'longtext', + enableConstraintsPerTable: false, + anonymousPrimaryKey: true, + explicitDropConstraint: true, + quoteIdentifier(s) { + return '`' + s + '`'; + }, +}; + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + dumperClass: Dumper, + dialect, + engine: 'mysql@dbgate-plugin-mysql', + title: 'MySQL / MariaDB', + defaultPort: 3306, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-mysql/src/frontend/index.js b/plugins/dbgate-plugin-mysql/src/frontend/index.js new file mode 100644 index 000000000..424f70e0a --- /dev/null +++ b/plugins/dbgate-plugin-mysql/src/frontend/index.js @@ -0,0 +1,6 @@ +import driver from './driver'; + +export default { + packageName: 'dbgate-plugin-mysql', + driver, +}; diff --git a/plugins/dbgate-plugin-mysql/test/testdb.sql b/plugins/dbgate-plugin-mysql/test/testdb.sql new file mode 100644 index 000000000..45f2e834f --- /dev/null +++ b/plugins/dbgate-plugin-mysql/test/testdb.sql @@ -0,0 +1,33 @@ +DROP DATABASE IF EXISTS `TestDb`; + + +/******************************************************************************* + Create database +********************************************************************************/ +CREATE DATABASE `TestDb`; + + +USE `TestDb`; + + +CREATE TABLE `ValuesTest` +( + `ID` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + `col_nvarchar` NVARCHAR(160), + `col_int` INT(160), + `col_numeric` NUMERIC(10,2), + `col_bool` BOOLEAN, + `col_bit` BIT +); + +CREATE TABLE `ImageTest` +( + `ID` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + `col_image` LONGBLOB +); + +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value 0', 0, 0, 0, 0); +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value 1', 1, 1, 1, 1); +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value any', 1241, 14.56, 1, 1); + +INSERT INTO ImageTest (col_image) VALUES (X'FFD8FFE000104A46494600010200006400640000FFEC00114475636B7900010004000000500000FFEE002641646F62650064C0000000010300150403060A0D00000A63000010E8000019F4000027A4FFDB0084000202020202020202020203020202030403020203040504040404040506050505050505060607070807070609090A0A09090C0C0C0C0C0C0C0C0C0C0C0C0C0C0C01030303050405090606090D0B090B0D0F0E0E0E0E0F0F0C0C0C0C0C0F0F0C0C0C0C0C0C0F0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC2001108006E00AF03011100021101031101FFC400E800000104030101000000000000000000000503040607000208010901000203010101000000000000000000000203000104050607100001040103030401030403000000000001000203041110120520211331221406303223154133250724341611000200030505040704080701000000000102001103213141120451712232131061810591A1425262231420C1723330F0B1E18292B234D1F1A2C2D24393241200000503040301000000000000000000003001112120403110501222410232611301000202020103030501010000000000010011213141516110718191A1B120F0C1D1E130F1FFDA000C03010002110311000001EC3E66DE66CE0DDB4FD476C69EAF0BA3DC4DAF2FD20EB7CCD681E948BADB53974AC46A2178F553DE7280A568326F76E124494661F4B40EB6F59C9135685C38755FABA9C89CDF6B267E0ED1DDE21CDAF2E46C4A38FD872EE16AD54279AB85E414CAC7B2DC09375DAB54B507D17F65C6C9324F6E654C9324C93C93DB952D3646DD3E4A6F194871F455FCB3D2AC5558049B308B10BE88FA3BEC39046EB24C9324C9324C93248E2DF5BD6C96E958D160807263548F1DB5CE178851B796A9D081E615C19FE86FB4875A87760A90FB2649A0DB51646336BA8B27509EFC8C0CA5ED134684408625B0CCCF10323C12A5C7511C182D6E6F57A4BDAF375022D624AC5C4A6C251D53E148D7071D0B38263D0E72F6E91C3E77F3DEBA0187A8396FF4E1C359C66784EBF1B30E4A0C73D9D1FEFF00931A1B182D6744344C7D10D13640C771920808ED45A4EAE69F29EE2969DE1AF3B08B14753A0711CA495B1F93B0F8DC4268BE90F79C58C8D85AB130C551B084D166929C621CA1D9E37A6F6E2FA2E6DE6FB001A9C91510B5D9ECC751AB6958B611135C7E1EC0E5BFAAFE85E5A309B0B0C44B154C428DB81E0B0D18DC5D0E7560BD7CEFE57DFD757DE62DAF60EB77BDAF7259334AE6A885653290FA7FEB3E62090C4C24696D4288229A285A9D11221B177E11EBDB42E0F4143F27D88CBD09332129180B5A1A3094848909BB067D5AF5DF1C6EB81E16956DA44EAE038F43923936851460AD2A87E57AA129D754A3A8C036B24696A7684192B33F869416134D3C6EDCF45E14C02E1C6C36228541F4CF60C629E56A3D91894E6AE5FA5439FDAD964BB89D184685CEAE8F9E71CB60CCDA0DE9E6F64FA4F9F0C85BDD28337AA670811316A882DAE2E87DDD358BA94EE0EFADCCEDA39DCAB45D315E4A484DB2D8E4D72DEEF98ED4EBF8B1D716905AD8B4ACB85EC01939094981B2BAE47E37A28F67DC217D68A61EE3B30D2EBCBA56EB6926FB3892EDDC3FFFDA0008010100010502E4EBBA4AF3C4D134A3626F1B7AD55FB3F3527D6380E4677D2ADF58E767FADDE8E46CACD398693C7F2BF5BE339F15DB38ADCB1CC1363717273C806D809B36147C86145CC4DB9DCBE64C052D0A9391C5D04D63183ECDF5D83EC9C73FFD65F63649C7FF00ABB90F99144D863D39899B5B89E3237C3C6FA2BB039E2CE4344B95238BDAF39779D4927664CF0BCCD7B3F173C7F92B01F95929F1EF5CD52304AFA16A479E3EE84FA3794ADB109F2E136505794E20B50D8FC36ED3C36092BC6985AE6CF7638899EE58069CE5BCAF1B2432399B53DA9966D5722F0B0AECD523745565F8FE786491B295BDBD05C0274ED0AE723142DBBCABAC9A6CDD0FCF99D3C14618DBB8045CC538E3E512D0FAD4A9FC07012A9FE94D9472FC1DDE0A1E2299711186B677E248EFCB126F30D4DE56AAFE56B27F2F50297EC15D8ACFD8A7914966494C5DCDBE44F1BC4F154BE1D7E4AFFC282FFDA2E6E9B9FBB223C95B726F313B1B5EF58B0D8F92E4ABAFB0F3373937556889BB9591FB8F28BD3A44E94A7CA9D22DC98A11DF903E4E545A6D6ADCCF2D35974B2B9C557AF0CD44B1D2337CB5DF07312B572AE8A49602D737B2B3FDC913D3D39393820144D55DAE0B9D7FC4E67EC1C8358DB5397B89D2AD975499F4E1B1C7EFCBFE47B8B7CC38BE4C2F3B0AB2C7191E14813DA56C28B11620C5137BB61AB141F68A8DB15B969E574AF39450EE3D56E7B14514B3282FCF564E479417220F21CDE4AEB5A62B4D5F24B5D6A932588420835D8A588B1CE8D062635548248E1E4A2F2D5E4F8E6D98ECD596BBD9812BE230D9B354D4B73C12577C67F7266491B9CE696B46508C95E43E4B75C491536CAC5256638FC5187578653244637C553728A0898BDF23DCD0E6D6B31BE4B35DA55CE132FBBC7D835F95DD2436EF41721A0DACE2E8A32BE0BEDB7F847EE8B898990CAC21EC76F6C9BD93319BD9FA4420992591813F3BA26FB429243BBEC7567A9760E48588FDAE921E56E51966E5AB4C1DFF009F72AF3F0555E79DAEA6E77909C44EF73642E6D86FEE47278DD6A3DE22F68DBB8C8CDED7537B5C611B236E0CFDA3970D64DCDC6FB17EAFF1B71B3EE06C32478DA578EB95E2AA9CC856435424CB21D8A6FD6EC23E877E5BE74376FEDF1D9E9EC526C530ADE1E636FC993FEAC5B97FC8CE7945FE517F9343F9444DFC70DA7FFFDA00080102000105027B501DC85E3241F63507ED39D4A2DCE93FA3B5C223404ADE74C2C68F6EE4617042158D4EAE190F1AEED31A63F3CCC458578CAD851042CACADC3F1176130E99D0B72A48F0B6E81C420FCA700B0B2B3D594E916729BE9958D328BDAB31ADB1946B829F096A7232649D3795BD7917911951712B1A37D1A148FDA1F69C512E2B0B6ADA50DC107922670EAC2C2C6A11F42EC09642F2D1A6DD30B2BFA4C3BA3D47560EF22B32A637A0B7B2C2F453316111F80040613CA95843C74808144E85835731616163A1A148DECE6870734B50EB3D18585844656106203A09C2D80A2D3A1282CE9B50C7E495B82E4C3DFCA42F205B9AB73579423315940E47E3749DDE308B70B72CAECBB2256534171DBD87E1EDA58FD43D0AECBDABDABDABDABDAA1C69FFDA00080103000105020ED72A9D7F9123C6C6CF4D92B1C3074614C94847D51EAC2C69959D2A5A3039BCA4443F956E1CEDC741AB7A31F9DA350EC7463528FE26B328B71A06656D016421A616D0B6A965C20C2B6A2D58E9DA99120CC27A6B517E98418E5E37AC3D79084E9BB451AC6BB178D78D788A1121181A15EA5EA18B798E9B536B342F105E208C61181855F8430B074E565656752A319219B9D5E00D40684F75EA8B17211F61F806AF2A12A9C49ADD5C32B3DD654AC0F6EC20EDEB1A651254632A9B83988A1AB8A2347B37AF169840E9959E82EC985D8746E31BA199B202B2B3900A27567747034051D32B38595B913A8F468CA6592D4D91B96A6B709C1611700BCF84EDC5DD27AAABFDAD6A93F4981AE02B90836508B252BE33936AB023D93863AC159E88E3F6C32640F70D8B0565CB2E432B6A79D80BFBFE1EEBBAA9FA7FAB33D76B3A7FFFDA0008010202063F02BC9DE6445A458C55218D7B66B6936099136AC2475B8C0C0C0C1FC54A6B7FFFDA0008010302063F02A27081BD507E86A1BC1EFA750FBCB20731D6B9D1ED518E62114E7219436C6E822D7920741DC669C8EA1D7263510AA83E87D09F65D64CE49929EDFF00FFDA0008010101063F02AF213F96D60DD19189A672D931057A641F695AEDE0C1A1A5A072E79A33599B74E293E9D65ADD44A969F35A158DAC4EE851A14CD57CCF4DD4D66A5AD2E6A4F3485C251A02287D3E93CC55535D41ADCC0192D559F1084A88668E26A7B754EA26F469B544DEA2714BCD943E9FCD3A4B5343AD46E5696649A34D48F08A0BAA75ABAA14D46A2A2890679711037C0F81E09ECB3085CF5002E6437EC89E689030C0A4917DAB2DDD0B3E5174EF9F666A9411985B3947F6A965B2944954281808FA3A953A1569B7534D5EFCADDE364044D4E9AA209F49C3B593EE2B6452ADE6DE614DE8AB035169166632C26D284A4962531251DBE65A87E5A3A5AAC7C14C79751A963D2D352471DE1003D9552CC847CB18CE1E5788BEE82B32B3F68586118DB92D5EE3B62D11C17C4E766C31D49F0ADA0E167E8E8793536969A9BA6A7CE1FE1539A9D1FE32267BB7F6EC30C40E0ADCBBF18CF4B4954CC5AD90CA38A905DEE9FF002832D3B34BDD21BFA498F9945D06D6523B2D1197D8C441E9939979E9BA9471BD5A47F42C9A66556F6F52DCA9BB69FD7BA1A9D207206CCD50DACECDCCEC71262686621917E6D65136A6BECF7B9B946F8F92B9A78A9E9D3FFD08CC7F844716A7A350F3350591FE66CCD07AAEF59BD977626712EC26857A94BF0311046B34E959AF1593E5D4F12A247C4422E89EA5466E7A751790EC983C5EA8AB51E7D52A4AA6339471F0554E47FDF16F177C5F2FB1699459C507AD5028F705F19138688B962A36218454D0689A597FBCD58FFAFE04F8CFAA1465B14E614C998CDEF19F337798B4C5AE137C65D45547118EF5631C1A8A94E7DF04E87CC15FE178A9A9D52008CD955B34C16C251F52F6BBDAB046D129C3458D31B23E62C620C7E718B6A93072A13B208A47A63BA26ED32604546A5FDE6A9FA3A41F111CDE114A9F33CF3D56C59CDE6198101B02DB60CAB112D96448EA180F447E7B7A6245986C9E305A95504ADEA6C3135CFDF94CE3CBFCBEAD46E9062EC97714003012EC6FB17FD911E5FA76E5D321AD23F17F943EA585A01C820BD47B4F2AE0076BD6A673550B6A2CEC6EFB63A62B639A4C25EBB62F34DC6C895651506DB8C7966AD4D932BE9BA04BC7B1BF42CCAB99C0F96BDF1A4AAF757D2B229C33A5A2134E9CAA8334A0F6AD45B56EA94F065D915ABE92A75594F569627BD0C677E3B78BBE32F4688960CB15180C8AA2765CA677C74EAD8EBC2EB19834C6D86E13E8EDBA2EFB0A2E04DA611688E2B0B3FBDE31A632E2A55785BD7F744EA5E40FB1283958A871C5237C37490D428B9980BE0D4A8895E763AD6133677DF6453A54687D2A4CB5645B98E18080F94671EDDB05455B0DE2422A4D81A865282B5E92D495F985B1F53A31F8E8FF00845A2DC62E8CA7C3EC67AC72AB7E521BF7C3FC0438F08B049AF53DF192AACB61C0EE84CDCB98669EC834AA83256B7BC40A2E66848937C2D069D4B089C2DAC0319365BE46F808C99325CA409C8DA337EF85013295E669CE7D8632B5BB218E5F98A384E313B95B082C2C0701072FA4C0A4E264581B6435337A9946673946CC604904F69B626E730864373020F8C56F2CD59C95A9B1142B9BACDB0F475148348F123081F466D7325A2C71D818FDF0AF5F4EF4355A4196B071CCA310718F2FD4133CF472B6F1099F4D2D6280A6B86B0F843AD5D2D5D5B9E5A74C7EA60E97E9D69D336297CD5186DCAC16174B4F474D0ACB3EB29A939945D9719ED8BBA482C0A6D7F1C22A2E2C84587F69869C03E9870703C3BB081BA0CF08CDB2F86C85733893D93F5F603D924C2F3035817E4D694AA0F785E0C04A9CE97137CA16466030FDB1528AD77E95362A00D80C7FF479669758CD633BD255697E2494717902A9F82BD61FEE8EAD0F2529545CDF5157D6271C3E55A7B394BA9A9FD64C65150504F769009FB2335462619828016F261A24794C4C732C2CF011286A23E58C76C364E24494DF7C4B1DBD83DEA8722FA270CE7D81387F2EF33A2ADA1AEB26AA27359E319695515E81E3D36A05A194EE89F75B05B309B45F16D7FF00498FCF6FE58E0666F0946C85A69693EAEF85A62CA62C9E3BE1A38A5E3D965D1C3F741CDCD8C3FE2E3DD0DEB8B21337BDC1BE462AF55C84CA736EF442DB3E0FBCC0152C59FC86EFC44196CB638413BE2C53EA8B9E3DB8B9E3E72F06315E5F992B7F0F67FFDA0008010103013F21714B51FB3D4C67326CBEF1B83452415903A0AB29BDD2B356B35E770E3E1085A8B4ED46AFC434807986FD79E8B95555BD92F98BBA73E2AAA97DC1F51E4337B47F12D9D3AB40C311ABFBA513C4768E80610AF83E6E79921F6996BB971A6963D449038DACA169F4816CAE6D4FF260994D0E3504A8638AB3D0CE2BA9640195F634458A4B3A8716147CAAB3DC1D6715158BEF30469805460EA466195C168C24A905A92368151BCD8F2B153558BF885FD0F58347ABCA1737C2F9860785AFBE046A08C3BA578B25FCCA5AAB697C47CACBB5C76770D1CCC884B3E8743CE6160DDCD46C963DCA0B6C793F30DFD7E69E4FF9B9E8519C7F7340E0F844B2732CE67668E6553D2CD55F4134CA823F252A7C2257B83344D337EC22740FFE449006A9F012F4C1E7995D0161C9FC54248ABCA4E9000F0D67FE261118B65CD3EDE8E7B67E7325AB4B28CC3D77D303A4A50029626BC85EC3049DA3297591F1BEF287DD7ABE2F4FACBC196D72F0AE2677494DCC09DEC7DC1958A3045DE0BE5DF30CB3E3070561ADEDEC9703A970C0E655D0D0B09666BF22563575DFF507E3E0E3F468DF733932F1AFACECB53E5E18BB34C1CF5739CC07DABFD983EA3156FAB30FD0DDCC4FDAF22E6D724F278A8D3C17A2294AADDEA0903C86A9F0CD951D455B58C157F32808F087DCA835647744D07C7136F659C03957CC283965EE8BF03F130FE412B065E522295FA2A94F0FCB3E8D5B9EF6133C5348CC40965AB301DB3219E1BCE5E0E7DEA166B39E55B47B6292FC6665A0396288D74627EBB65824B603FCA5A5A1CEE64BE20B36707CA2F10A721E63776DDA7C444677F262D3C1708DA294BCFB5FE0955C73D1ED25B2E975965F4841948345AC3F643A7608C550686240DD5C56012D15E3AB2D7CDEBC4D009514DD55D204436356739E254F41FF00F197BF4AEE88FD08CBD97411D23FB3F827283730B89607D05B8AD4E74E0D2F05F8CCB4F5EDC8D7E6ABE6534EDACAA5B2CAF9974DC38B996C6D410E9D1844DDCE3822B726C16BD988212A2704305ED073C40A24117680067B87BD03412B9094B05CA3411607D72E89BCE6291C349F68AF3FA7A8E7D435AD41B68EE72A69E58738D7766804D40CE784AAB3E92C2BCC507459C40500B5C01018A7C18074D6E3A544C800E436E7A9790745671DB1FDAA1912682DD5B1A2007B364FBE6A5330E84CAF6007E6036E1657C7B4B43F0020F1750162D63CA770238161C7BC6F5F23FDC55D22BB1D32CE21DC096524BE33F02099BD43B7FD4BE6CE9E4E4F91E49939B92C876A63F1EE46599524B29BB767B92E149ECAB3EF4C20ED87AB1E3DCA6301425FA7801EE4BA975894D032FED8950FCE726D71A3E3D2088168B62F692A09E2E64DAD1F4CD798237A79BE5C6311485B72C02CBE58E8468DCF2C37C4CE6BDF9AE7E62D50BAE4FEA5F985BD8B3DE535D4E15E7CC117E9D0A84E5D7D64C0FCF716C115C64E7FD817EAC18178B4E7FF0052CE08815ACD4F70CC91882F3DBF23F696BD8E3236B5DB330E2BEC1DA58F8968E698FF0002293811E751D354B91814D1CBF339EE9217072F45EBED2BBE9686CDD16FD2BC4C5CE3F04EE5D7BA5D36AFF7B3160F057DE558F708A1682AF795AC535127C1A651534E4967218E9A97980F90C7E0DED00ABA0B57006B0AD278BC9E38E22D68CDF202FEB2E983ED28AA7091F5717B5F2316759D0FD17284E2998AE4D92532ABB023DE1FDA5AB1C552BE24DAC85DB6AB0E8136378D1E63349D7E09903BFC7994657F70EA6233006BC11043973ED298D837E5E6C8301224C17C261991F7C6F1730155D37CD93F4198214BD3477027488144A5BC0F264DCB6AE6A9D99C2CD3050E0B57930C655B5A0F3F316E55D99FEA7313C12FDBFF00B8032BC581F99CCAF26234272DE9CAF69B93EF90ED799C6DE3F04D35F0C2537C33372E9532BF547E604E057DDC399DAEA936D3E272D5FB07C5C5D94B25E39694028D0CF8C0CCF112799EA7EF89EC56F8453ED423C4FC7FA97FF9BFB9EDFB62E0BF48CCA391AF8E66BD5539EFF7DCCDCFFFDA0008010203013F214CCA3D16008E8A9858DEE4A4BF5ED730824C2150BFA75D421B997713D149504D443132C107EAC65F41823519494CED0952BF557A73FA2B62F8F458F0108210C7FC822CB611ACB589CA343488CE4A0689B1A46EDE8FCCF385A5FE8B23246E5A4C58C10A7A248DDB15F49B2413319CE88BE0F45A4A279CAC4C5E3D38922A8E4837446A366CBF7070F48AD784254951F4A95EBBEBC99840FA6809E95A851E8112ED136F8827FC07D609C5F40F42036F4BCB84A9622FEA7D40188055EA38FA20A811F41F41080CC5545B2A5F72CCFAB69E81E8F5996454BE111AF42A566A2543D70972A25C292929061772DDC125FA76A6A6447F8884327A5917A37A26D98698F4E7D398FAB065FA25BC469ED0E0C0966C33F6D81FFA9D0237C4CA5C56A1E8465C2312557A1297A20AAE25DB217819EE97DA0466B1027E88FA139FD0C4A4764F295E95454D7A1D1E9FFFDA0008010303013F21C5E970804F79E65FF840713123130C565B3D6866659E929A6A07307E83D11E8BF52E50E58EE5B1B3E20551BF31916DF5DBD08B1E84A8454A951FF9D9FA20DCA95E8A95E84113FE2914EA546CB886D7FB1E29484031F49E41156B71EBE85BF4541B118466D05382332A0E700874CA764E0210EB72D6DB95A844195E25A5E10EDF4EC7A130C5C7A3D6D4E1A07C45982D9372432E529257A8C20F4081FD114A901A251E8A67399881751BC17D350F425FEA6395B2DCE507AD08548CD4A7715734125B2117E87A0C3D171710C62D6CCC3C9C6FDE11D1163D1F449949369DC054B2784AF0CBF417972E5CD47A47D7D9DCB47D1C9E92D2F612E3D0DEA370C62DF3125115CBF45BD559000975997A7CD361A660CE3B8FC041B22AF11FDA7699163150B62225F0E883184D4DCD207A54A9505ED20B925EDDC04A667E53269BED260DFB65D0645CFBCA8A258393C423063E9703D47A3B7E12E70C650CC4944A389E296EE1056457F478988C37E998431693AC502D45F12FC4BF12FC4B62C6F51B9FFFDA000C0301000211031100001009CCE8E3F307D1E02D403E0EC85FFD389B20492811FEA747B60000000A04ADFA45A74012BA857F0D683CB8BA68EFCC5D152412FC91D53E7A0ECCDF9C66610B29B3F92E6F081C5E9D2FC044B5ED24511E7B872C983CA1BBF3A258D77550407880D497C65E69C331486C1F53BBEEC7A977DD41756FFFDA0008010103013F10AF3BAC2BCC40BCB84CBCE7D470B25AD801AA235CC8C6B5AA655A8D4966DCD0102433D10F3332E738152A42D4BBB0A8CB3C50CD2AE2ED3E23686675C406CBA1D695016607E8763F497F4F4BC317D49C35BB4C132F2A9B416694A9714F55EF8ADDAA50E23B88054AE01982E00BF1FE9666AC46C7564336042B4F4CA8DF264D02B2D3C1D46AC5C84C0B7540283063DE2AD256C8C0AB33DEEE36B8A4C05442AA2C46CABCC71BA2A8745800380DCAD14294589D54A6C9DB74C74D2B87183908BF9AA0B62C9C706B3A97CCE0B25D5D01CD1034CA875772D4D8D64A4186B7C5E83ADCED5A77A56387A9CFF4BF612156B9422242940E80E00C109CFF0031F4A0A01B60BE5507962116A6EC38DD384812EE5F62475199178426E3118C1ACB7D5F35116F20B6D393B8CA6AD6104C135754F8814B7253891B2A8138BB993052EC36F66BEF1E5052A8BD25D022B5759330CB003928EF7A7B4648DF0A14B4DD598DEA1A254AFD15E95DFA2C09000B98315EA6F4D256A976B7CC5907447818589D55917BA6C141101C2E4F7943105C719C2E959583A2768540720E9127631B3CA915ED3740E15BE930660CA40004A038D930B9AD31A3C2408CA1585E002E292EDB3E78C12F4C9A4E9CA5383FF14C00E42C5882AC5AECB688E569E965AD01ACBEC514439D781ECE11A47DE27C309190D633414D8A2638A287F2DE9D393BD653891AEED15BF69BE88F14C883AB9AE193FF0066A5948DF8C71328198AC22F4F2CA8108E30C6428DA34B6D173EC4894395D6C05642D8C73014E0C559165A65AC13832EAC3A87904AB30F22420E5855423B4295F8A848BD8511F59655DE3D6AB35A00BAEAE04847656163B5FC5CA61D6215B8286F9CE894FB9B3926133F130EAA2BC17F1EE2007A3B442B2D73B443034228D736035F146D757044587542ABE0332BE32F2873485B15BFE4DBDD6F8F98A59AE01EABD94E7A82D3E85C94C675FA44D8E42CAE97039388BDF9D32E9716250F97B8502E928A07409DBF06620E5E3E14E3AFDDCC9B49FC70D79995D08560F68D4A9F73F4BACF881566D8A93BC950DA074D9F985507E08FCC35B8058A1C675D7503A11D65129B5A658F700ADBBCB98D71C10BEECD0098E83F600398754210451D5B651047A0021D0FEE3C06621E105C26ECDD59C0D130CEBE079ABC9AF68584C1D1001F632E91175BD942A1871BE49562E38B8A5253C372B64230855C095531D420893A140A2AD10756CAC5E06F556DFC4B735825544CCF2C11A73A894DB097A3AB6A28ABBAE238732E29954B2EA37C7C67F88880E178317CF010B614A29701BAE6BF11C19A67D3C81A1AAD16CC0052D0B7CEDDB056CAC17C5A317B0B5B3031B4CC01938CCB28A38712A67F8000052B488C7C84060F794CBE9F32BB585ADB3CD580E76C21B8E19C2EF3EF72C70CE2254EF9C88DA55090AE30C438CC4AE630A73348C4A26DB0D715CE416A55064A065669C903C220387B000AEF36E7E222A1B754CB59D57B444AC58C952F008470D9AB30E623061A6B94A1938130360333C0A6540E6D4428AC68D4278B6D4141908366D6AE2714F0D00DB05C295DCB40153C8559193DA612ECA50CAAF550558EE21F111AD0A0D23C32C14C731380F058F2B4F76899B3319B2A103CA1DB7344320A0DD198D96882068DB4529BB28A87F562A5AD42A737C0CCA680EB9A7F0163F1DC75194AFBB1F352E11C8BB67DEA2927D15AAE0023FA06603114033A661A1294219A1180ACE0EAECB382C182DA3E11F215790573306ECCE3A223E661935DE19BE4826DF66D0889C37A090B2D41C0154B699C57DE0AA957ABA1D3DC63C9D8250348E5C3B3E4EA2EBF7B042C3A71EF2EC5274DB3EE3ED0A6CD1CA0D617D9FD404A46D0D432510212235AD9774D7A39DFBD5896E2F91F1663EA984AB5481F08CDE4CE23C71FE398E13BE4D34C6C9500324173D9600015EF456D2F292DC35356156AB01F025F51358ECE897B140F4C51D3026426A16894E3B949102D9A0ACA8F9551C085EF001264AA011A0739BAB949120131EA2E836B5C4B5A0C2D44EF2DB5CCCD3F9B978B90978EE3CB6459C85314DF399822CA005E5ABBE639EF5DE614DE28226B0B14D28C844B4225F106EB2809928039AD885E500CA0CE6DC7D0DC007112E404CABE26E162C9713073835C543EC88F685F66668E1E152FB004AE179C592E0323A855834DD6823D32F88CE85000053811EF7114054BD83CB8691036E750959E9091119767374AA35539393B032A2B2E2F78087278181C4A618A64F578C29284158AE186B42DD394157F448C2EE200DE11B6D528D5E2F04716B345AD691995CF72B0649CF824152E357F8A1A68B30D60E1F6EE01A12EBC960FB7DE7165F4945EA540382F263DE0922A7305130F6FF70BA82038AE45E429311393014FD33F2445722D9ECA4524511E95DA42DE40B38950C58A014CD0A3B35A6ABC14FA9354341D8BB70C4A88012C145041A073097008A1083681A65B38E96CD2ABF58E3BC0D62F80CFA41B9A06B7021BA582DAB242E05E59C436303998D955EC82DCB02DA2C2D6F79CC430C53884B8CB44BC97A85C9D54E03DB8A7E6BF8877D0B136BBF8392509511E4D1B78B9BCCB19C726EA55684D68E15C88C2D552D4946EDBFBEAEE0B05BFED435A99A19E9F88A84252762378A74450172A61752B51EE7FE64046C3BC0942EA559380118156BD32B39A0495556F6DAD1C71D621265B402BDCAABE63789D13ED5FCA00BF320FD6E57C8F855F787C05AB38EF367ED14E41D95F998BD3A0839A9C0B581D90AC6510BC656EAA8D4BF334C95F339FA4BAB97FC21BB989DF2DEDCEB31E1AF5F7EB734372F371F15D54799B4C2B46BB6EAA02D06728305E1179BEA06D57DBFC428B4B617DED7DEFCC36228B64111630670412B416296311C591074D9F09D4B6936B472E232537B5D77DCB6117918BE369F796189C2D15B61ECCAEDC321CB1D2FF0062535D259BFD09DCDFBAAAB4B9878C30F1789D97778F79FFDA0008010203013F10441D09B2355562605A0EF98DCE3F6EE146F365ABB610746C7F304CC3E8F4C2987814EC40D17B978F129583300CB12E9326258DA5F3E8EA381A437300EC259C1001896DE200159DC7A2AC2504C20B750350D4711DC956512A67CFA490998A372A311B65323F4152A25C0A9E5E853487AAA2699454E07C7DC9D7FC4D923E22752CC4CD1CE28F787FC48BE25B4884C42C36C601334F6FF63D957E58C36C26982614431BE4298F2C70CAE3F7EF299E1E7F873316A2A599257F425B609820B4B3D117E75D4A94B9CCFE21025CDDB3000CDC54E3BF7F466E88AF11C70FBC9FC10F298263E60B619430DECC4EC4F0CA78333404F024B9B63065DBD4473E63A5E9032CB3B14194257BC5E9CC5A97E2368BC0F2C168BA890B99BC423094CC4C4C2620B52827022A8AE65C1D4ADDE2090082B7684495532CCF272A3787E8CB6EDA0040C4DA54A8912BD3020CD91198CA622D5FB4A123416EE008151D37150D903144AB9824517DFB7994D5AD8CE719622253E94D5C73895E9A80D5CAC6911E434CEF665767AC1531C7A08EA2992E50815363C8F10235A8D0C44DD0BF42748F84454C4B82EE1E20ECE88BED660A6A79752807CF0C71A40974B95054D6E62381AD10A421551A546166A60120111328D2914EE39C304BD91C30D55E07F89B17CBFB99285625E45735F88035983C8B02E12BDE526BE522DB3A3AE6098314DBCFA55425CA081F4F433C438961083B9D8286539FC251F5194B818FE305457FD23767DE81FF00AA604A1E08A56B1B840DBF11D912E5B984F996AA87312344C706A34CCB708C97371C6D4C31A022CCBBCC214F28070DFA2486A263AAAFACD3D6D79F4B71335BEBD1AE655A78C1477516269C3FBCC05416E595DBF30E47F33C9F9876FE625709F7872E5FC7A7FFDA0008010303013F102C23316A11F0C2458AE9E541E079654D08D0553BFA67AF128B4A28C251A7B3C3ABC6728E3494FC7AE01D301A0E6471CD3C7889B4D5B1E0971941E612AE524B4018A95208828A8A633A0A8765DE3C91603748BF8CB7055E8A2807D97DE6D24B7D7110D41701A9BE62430A95E80FD0C6C7FCEF5BA8FA22CC73613CD253B87942308311405E621FF17F24A8114302345CBFC1CCE97F7FA344672AF3FE46F204A24DF7A0B061BF782EAB9350D0B3DF895159100D6225C7E813A26FB11D10F97F897EDBDC775F13862D1DC49AFDF83A3DA5971B8BC02C6EC879775823291A20DB1500A7A36D16CA8BE9B9E32712A772651CB00189AC738131D12A0AB2F3043F282E0DFB4D30964A23F50D69F485681B7A25AE10875E8BC2B3BE241B8A8970F8608E6770287CF70C4BA800D25D59220789908C45C0FCEA1C4BB34F450A306C81515C19751CA8E600DF224CA0CAE3DA1628C733191C855E18A1F11FFD111FC0C7BF0C34D129FEFE626710109841B6714136F490B8C9619F187C4DC86039205625A24B72DCBB8473529021BBAEA27BB5F7EA16050E6295EFB97399472AA5AFBB3683210B2187A12198341BADC12F38FACDA93E83FB96BB3CAD9E497AB08ABCC08ECA81771FBB96445DA8AB2AB64B815580F2AD07CCE62588103D5CB770159A858DCBC71965583304A9B33F481654429DF997BC2531F59B79C0FF0024CAE0F9CFB435AB98829CB8863083B6618375D626BF1D3C4465472EA521C44725E3EF2C20A60C4729B4CC4B92F47A611E908076FE88CAB4F5C3012AA149032E037AFC4FB0A24FA3308A9E521BA1ED8ECAF9AFC45E8B792FE61A0CE5442BE5858880732AC333835302E7225AF3A8D246AF88B507500EBDB886028D939D10558E222C54702AFCCF07EB07008EE562D709AD88FD1B98807A18E7713BCAD1D4A6B18758B2A8A998ECEAE617CDE498B4627171FF00F52FC0FB7F717D3EA45F4FA90E0227057C22F97FB3FFD900') diff --git a/plugins/dbgate-plugin-mysql/webpack-backend.config.js b/plugins/dbgate-plugin-mysql/webpack-backend.config.js new file mode 100644 index 000000000..87b665d56 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/webpack-backend.config.js @@ -0,0 +1,23 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-mysql/webpack-frontend.config.js b/plugins/dbgate-plugin-mysql/webpack-frontend.config.js new file mode 100644 index 000000000..db07de291 --- /dev/null +++ b/plugins/dbgate-plugin-mysql/webpack-frontend.config.js @@ -0,0 +1,24 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-postgres/LICENSE b/plugins/dbgate-plugin-postgres/LICENSE new file mode 100644 index 000000000..c15ede038 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jan Prochazka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/dbgate-plugin-postgres/README.md b/plugins/dbgate-plugin-postgres/README.md new file mode 100644 index 000000000..b12dfd03d --- /dev/null +++ b/plugins/dbgate-plugin-postgres/README.md @@ -0,0 +1,6 @@ +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![NPM version](https://img.shields.io/npm/v/dbgate-plugin-postgres.svg)](https://www.npmjs.com/package/dbgate-plugin-postgres) + +# dbgate-plugin-postgres + +Use DbGate for install of this plugin diff --git a/plugins/dbgate-plugin-postgres/icon.svg b/plugins/dbgate-plugin-postgres/icon.svg new file mode 100644 index 000000000..6b65997a9 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/icon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/dbgate-plugin-postgres/package.json b/plugins/dbgate-plugin-postgres/package.json new file mode 100644 index 000000000..25033b085 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/package.json @@ -0,0 +1,39 @@ +{ + "name": "dbgate-plugin-postgres", + "main": "dist/backend.js", + "version": "1.2.2", + "license": "MIT", + "description": "PostgreSQL connector plugin for DbGate", + "homepage": "https://github.com/dbgate/dbgate-plugin-postgres", + "repository": { + "type": "git", + "url": "https://github.com/dbgate/dbgate-plugin-postgres.git" + }, + "funding": "https://www.paypal.com/paypalme/JanProchazkaCz/30eur", + "author": "Jan Prochazka", + "keywords": [ + "dbgate", + "dbgateplugin", + "postgresql" + ], + "files": [ + "dist" + ], + "scripts": { + "build:frontend": "webpack --config webpack-frontend.config", + "build:backend": "webpack --config webpack-backend.config.js", + "build": "yarn build:frontend && yarn build:backend", + "plugin": "yarn build && yarn pack && dbgate-plugin dbgate-plugin-postgres", + "plugout": "dbgate-plugout dbgate-plugin-postgres", + "prepublishOnly": "yarn build" + }, + "devDependencies": { + "dbgate-plugin-tools": "^1.0.4", + "dbgate-tools": "^4.0.3-rc.1", + "lodash": "^4.17.15", + "pg": "^7.17.0", + "pg-query-stream": "^3.1.1", + "webpack": "^4.42.0", + "webpack-cli": "^3.3.11" + } +} diff --git a/plugins/dbgate-plugin-postgres/prettier.config.js b/plugins/dbgate-plugin-postgres/prettier.config.js new file mode 100644 index 000000000..c05d71875 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/prettier.config.js @@ -0,0 +1,9 @@ +module.exports = { + trailingComma: 'es5', + tabWidth: 2, + semi: true, + singleQuote: true, + arrowParen: 'avoid', + arrowParens: 'avoid', + printWidth: 120, +}; diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js new file mode 100644 index 000000000..8f3d92b15 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js @@ -0,0 +1,185 @@ +const fp = require('lodash/fp'); +const _ = require('lodash'); +const sql = require('./sql'); + +const { DatabaseAnalyser } = require('dbgate-tools'); +const { isTypeString, isTypeNumeric } = require('dbgate-tools'); + +function normalizeTypeName(dataType) { + if (dataType == 'character varying') return 'varchar'; + if (dataType == 'timestamp without time zone') return 'timestamp'; + return dataType; +} + +function getColumnInfo({ + isNullable, + isIdentity, + columnName, + dataType, + charMaxLength, + numericPrecision, + numericScale, + defaultValue, +}) { + const normDataType = normalizeTypeName(dataType); + let fullDataType = normDataType; + if (charMaxLength && isTypeString(normDataType)) fullDataType = `${normDataType}(${charMaxLength})`; + if (numericPrecision && numericScale && isTypeNumeric(normDataType)) + fullDataType = `${normDataType}(${numericPrecision},${numericScale})`; + return { + columnName, + dataType: fullDataType, + notNull: !isNullable || isNullable == 'NO' || isNullable == 'no', + autoIncrement: !!isIdentity, + defaultValue, + }; +} + +class Analyser extends DatabaseAnalyser { + constructor(pool, driver) { + super(pool, driver); + } + + createQuery(resFileName, typeFields) { + let res = sql[resFileName]; + + if (this.singleObjectFilter) { + const { typeField, schemaName, pureName } = this.singleObjectFilter; + if (!typeFields || !typeFields.includes(typeField)) return null; + res = res.replace(/=OBJECT_ID_CONDITION/g, ` = '${typeField}:${schemaName || 'public'}.${pureName}'`); + return res; + } + if (!this.modifications || !typeFields || this.modifications.length == 0) { + res = res.replace(/=OBJECT_ID_CONDITION/g, ' is not null'); + } else { + const filterNames = this.modifications + .filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change')) + .filter(x => x.newName) + .map(x => `${x.objectTypeField}:${x.newName.schemaName}.${x.newName.pureName}`); + if (filterNames.length == 0) { + res = res.replace(/=OBJECT_ID_CONDITION/g, ' IS NULL'); + } else { + res = res.replace(/=OBJECT_ID_CONDITION/g, ` in (${filterNames.map(x => `'${x}'`).join(',')})`); + } + } + return res; + + // let res = sql[resFileName]; + // res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null'); + // return res; + } + async _runAnalysis() { + const tables = await this.driver.query(this.pool, this.createQuery('tableModifications', ['tables'])); + const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables'])); + const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables'])); + const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables'])); + const views = await this.driver.query(this.pool, this.createQuery('views', ['views'])); + const routines = await this.driver.query(this.pool, this.createQuery('routines', ['procedures', 'functions'])); + // console.log('PG fkColumns', fkColumns.rows); + + return this.mergeAnalyseResult({ + tables: tables.rows.map(table => ({ + ...table, + objectId: `tables:${table.schemaName}.${table.pureName}`, + columns: columns.rows + .filter(col => col.pureName == table.pureName && col.schemaName == table.schemaName) + .map(getColumnInfo), + primaryKey: DatabaseAnalyser.extractPrimaryKeys(table, pkColumns.rows), + foreignKeys: DatabaseAnalyser.extractForeignKeys(table, fkColumns.rows), + })), + views: views.rows.map(view => ({ + ...view, + objectId: `views:${view.schemaName}.${view.pureName}`, + columns: columns.rows + .filter(col => col.pureName == view.pureName && col.schemaName == view.schemaName) + .map(getColumnInfo), + })), + procedures: routines.rows + .filter(x => x.objectType == 'PROCEDURE') + .map(proc => ({ + objectId: `procedures:${proc.schemaName}.${proc.pureName}`, + ...proc, + })), + functions: routines.rows + .filter(x => x.objectType == 'FUNCTION') + .map(func => ({ + objectId: `functions:${func.schemaName}.${func.pureName}`, + ...func, + })), + }); + } + + async getModifications() { + const tableModificationsQueryData = await this.driver.query(this.pool, this.createQuery('tableModifications')); + const viewModificationsQueryData = await this.driver.query(this.pool, this.createQuery('viewModifications')); + const routineModificationsQueryData = await this.driver.query(this.pool, this.createQuery('routineModifications')); + + const allModifications = _.compact([ + ...tableModificationsQueryData.rows.map(x => ({ ...x, objectTypeField: 'tables' })), + ...viewModificationsQueryData.rows.map(x => ({ ...x, objectTypeField: 'views' })), + ...routineModificationsQueryData.rows + .filter(x => x.objectType == 'PROCEDURE') + .map(x => ({ ...x, objectTypeField: 'procedures' })), + ...routineModificationsQueryData.rows + .filter(x => x.objectType == 'FUNCTION') + .map(x => ({ ...x, objectTypeField: 'functions' })), + ]); + + const modifications = allModifications.map(x => { + const { objectTypeField, hashCode, pureName, schemaName } = x; + + if (!objectTypeField || !this.structure[objectTypeField]) return null; + const obj = this.structure[objectTypeField].find(x => x.pureName == pureName && x.schemaName == schemaName); + + // object not modified + if (obj && obj.hashCode == hashCode) return null; + + // console.log('MODIFICATION OF ', objectTypeField, schemaName, pureName); + + /** @type {import('dbgate-types').DatabaseModification} */ + const action = obj + ? { + newName: { schemaName, pureName }, + oldName: _.pick(obj, ['schemaName', 'pureName']), + action: 'change', + objectTypeField, + objectId: `${objectTypeField}:${schemaName}.${pureName}`, + } + : { + newName: { schemaName, pureName }, + action: 'add', + objectTypeField, + objectId: `${objectTypeField}:${schemaName}.${pureName}`, + }; + return action; + }); + + return [ + ..._.compact(modifications), + ...this.getDeletedObjects([...allModifications.map(x => `${x.schemaName}.${x.pureName}`)]), + ]; + } + + getDeletedObjectsForField(nameArray, objectTypeField) { + return this.structure[objectTypeField] + .filter(x => !nameArray.includes(`${x.schemaName}.${x.pureName}`)) + .map(x => ({ + oldName: _.pick(x, ['schemaName', 'pureName']), + action: 'remove', + objectTypeField, + objectId: `${objectTypeField}:${x.schemaName}.${x.pureName}`, + })); + } + + getDeletedObjects(nameArray) { + return [ + ...this.getDeletedObjectsForField(nameArray, 'tables'), + ...this.getDeletedObjectsForField(nameArray, 'views'), + ...this.getDeletedObjectsForField(nameArray, 'procedures'), + ...this.getDeletedObjectsForField(nameArray, 'functions'), + ...this.getDeletedObjectsForField(nameArray, 'triggers'), + ]; + } +} + +module.exports = Analyser; diff --git a/plugins/dbgate-plugin-postgres/src/backend/driver.js b/plugins/dbgate-plugin-postgres/src/backend/driver.js new file mode 100644 index 000000000..087c9a332 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/driver.js @@ -0,0 +1,213 @@ +const _ = require('lodash'); +const stream = require('stream'); +const driverBase = require('../frontend/driver'); +const Analyser = require('./Analyser'); +const pg = require('pg'); +const pgQueryStream = require('pg-query-stream'); +const { createBulkInsertStreamBase, splitPostgresQuery, makeUniqueColumnNames } = require('dbgate-tools'); + +function extractPostgresColumns(result) { + if (!result || !result.fields) return []; + const res = result.fields.map(fld => ({ + columnName: fld.name, + })); + makeUniqueColumnNames(res); + return res; +} + +function zipDataRow(rowArray, columns) { + return _.zipObject( + columns.map(x => x.columnName), + rowArray + ); +} + +async function runStreamItem(client, sql, options) { + return new Promise((resolve, reject) => { + const query = new pgQueryStream(sql, undefined, { rowMode: 'array' }); + const stream = client.query(query); + + // const handleInfo = (info) => { + // const { message, lineNumber, procName } = info; + // options.info({ + // message, + // line: lineNumber, + // procedure: procName, + // time: new Date(), + // severity: 'info', + // }); + // }; + + let wasHeader = false; + + const handleEnd = result => { + // console.log('RESULT', result); + resolve(); + }; + + let columns = null; + const handleReadable = () => { + if (!wasHeader) { + columns = extractPostgresColumns(query._result); + if (columns && columns.length > 0) { + options.recordset(columns); + } + wasHeader = true; + } + + for (;;) { + const row = stream.read(); + if (!row) break; + + options.row(zipDataRow(row, columns)); + } + }; + + // const handleFields = (columns) => { + // // console.log('FIELDS', columns[0].name); + // options.recordset(columns); + // // options.recordset(extractColumns(columns)); + // }; + + const handleError = error => { + console.log('ERROR', error); + const { message, lineNumber, procName } = error; + options.info({ + message, + line: lineNumber, + procedure: procName, + time: new Date(), + severity: 'error', + }); + resolve(); + }; + + stream.on('error', handleError); + stream.on('readable', handleReadable); + // stream.on('result', handleRow) + // stream.on('data', handleRow) + stream.on('end', handleEnd); + }); +} + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + analyserClass: Analyser, + + async connect({ server, port, user, password, database, ssl }) { + const client = new pg.Client({ + host: server, + port, + user, + password, + database: database || 'postgres', + ssl, + }); + await client.connect(); + return client; + }, + async query(client, sql) { + if (sql == null) { + return { + rows: [], + columns: [], + }; + } + const res = await client.query({ text: sql, rowMode: 'array' }); + const columns = extractPostgresColumns(res); + return { rows: res.rows.map(row => zipDataRow(row, columns)), columns }; + }, + async stream(client, sql, options) { + const sqlSplitted = splitPostgresQuery(sql); + + for (const sqlItem of sqlSplitted) { + await runStreamItem(client, sqlItem, options); + } + + options.done(); + // return stream; + }, + // async analyseSingleObject(pool, name, typeField = 'tables') { + // const analyser = new PostgreAnalyser(pool, this); + // analyser.singleObjectFilter = { ...name, typeField }; + // const res = await analyser.fullAnalysis(); + // return res.tables[0]; + // }, + // // @ts-ignore + // analyseSingleTable(pool, name) { + // return this.analyseSingleObject(pool, name, 'tables'); + // }, + async getVersion(client) { + const { rows } = await this.query(client, 'SELECT version()'); + const { version } = rows[0]; + return { version }; + }, + // async analyseFull(pool) { + // const analyser = new PostgreAnalyser(pool, this); + // return analyser.fullAnalysis(); + // }, + // async analyseIncremental(pool, structure) { + // const analyser = new PostgreAnalyser(pool, this); + // return analyser.incrementalAnalysis(structure); + // }, + async readQuery(client, sql, structure) { + const query = new pgQueryStream(sql, undefined, { rowMode: 'array' }); + + const queryStream = client.query(query); + + let wasHeader = false; + + const pass = new stream.PassThrough({ + objectMode: true, + highWaterMark: 100, + }); + + const handleEnd = result => { + pass.end(); + }; + + let columns = null; + const handleReadable = () => { + if (!wasHeader) { + columns = extractPostgresColumns(query._result); + pass.write({ + __isStreamHeader: true, + ...(structure || { columns }), + }); + wasHeader = true; + } + + for (;;) { + const row = queryStream.read(); + if (!row) break; + + pass.write(zipDataRow(row, columns)); + } + }; + + const handleError = error => { + console.error(error); + pass.end(); + }; + + queryStream.on('error', handleError); + queryStream.on('readable', handleReadable); + queryStream.on('end', handleEnd); + + return pass; + }, + // createDumper() { + // return new PostgreDumper(this); + // }, + async writeTable(pool, name, options) { + // @ts-ignore + return createBulkInsertStreamBase(this, stream, pool, name, options); + }, + async listDatabases(client) { + const { rows } = await this.query(client, 'SELECT datname AS name FROM pg_database WHERE datistemplate = false'); + return rows; + }, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-postgres/src/backend/index.js b/plugins/dbgate-plugin-postgres/src/backend/index.js new file mode 100644 index 000000000..4a2c8d0cd --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/index.js @@ -0,0 +1,6 @@ +const driver = require('./driver'); + +module.exports = { + packageName: 'dbgate-plugin-postgres', + driver, +}; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/columns.js b/plugins/dbgate-plugin-postgres/src/backend/sql/columns.js new file mode 100644 index 000000000..841eda6b9 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/columns.js @@ -0,0 +1,19 @@ +module.exports = ` +select + table_schema as "schemaName", + table_name as "pureName", + column_name as "columnName", + is_nullable as "isNullable", + data_type as "dataType", + character_maximum_length as "charMaxLength", + numeric_precision as "numericPrecision", + numeric_scale as "numericScale", + column_default as "defaultValue" +from information_schema.columns +where + table_schema <> 'information_schema' + and table_schema <> 'pg_catalog' + and table_schema !~ '^pg_toast' + and 'tables:' || table_schema || '.' || table_name =OBJECT_ID_CONDITION +order by ordinal_position +`; \ No newline at end of file diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/foreignKeys.js b/plugins/dbgate-plugin-postgres/src/backend/sql/foreignKeys.js new file mode 100644 index 000000000..fadcf5e78 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/foreignKeys.js @@ -0,0 +1,24 @@ +module.exports = ` +select + fk.constraint_name as "constraintName", + fk.constraint_schema as "constraintSchema", + base.table_name as "pureName", + base.table_schema as "schemaName", + fk.update_rule as "updateAction", + fk.delete_rule as "deleteAction", + ref.table_name as "refTableName", + ref.table_schema as "refSchemaName", + basecol.column_name as "columnName", + refcol.column_name as "refColumnName" +from information_schema.referential_constraints fk +inner join information_schema.table_constraints base on fk.constraint_name = base.constraint_name and fk.constraint_schema = base.constraint_schema +inner join information_schema.table_constraints ref on fk.unique_constraint_name = ref.constraint_name and fk.unique_constraint_schema = ref.constraint_schema +inner join information_schema.key_column_usage basecol on base.table_name = basecol.table_name and base.constraint_name = basecol.constraint_name +inner join information_schema.key_column_usage refcol on ref.table_name = refcol.table_name and ref.constraint_name = refcol.constraint_name and basecol.ordinal_position = refcol.ordinal_position +where + base.table_schema <> 'information_schema' + and base.table_schema <> 'pg_catalog' + and base.table_schema !~ '^pg_toast' + and 'tables:' || base.table_schema || '.' || base.table_name =OBJECT_ID_CONDITION +order by basecol.ordinal_position +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js new file mode 100644 index 000000000..7cc2141e9 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js @@ -0,0 +1,19 @@ +const columns = require('./columns'); +const tableModifications = require('./tableModifications'); +const viewModifications = require('./viewModifications'); +const primaryKeys = require('./primaryKeys'); +const foreignKeys = require('./foreignKeys'); +const views = require('./views'); +const routines = require('./routines'); +const routineModifications = require('./routineModifications'); + +module.exports = { + columns, + tableModifications, + viewModifications, + primaryKeys, + foreignKeys, + views, + routines, + routineModifications, +}; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/primaryKeys.js b/plugins/dbgate-plugin-postgres/src/backend/sql/primaryKeys.js new file mode 100644 index 000000000..204ff8478 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/primaryKeys.js @@ -0,0 +1,17 @@ +module.exports = ` +select + table_constraints.constraint_schema as "constraintSchema", + table_constraints.constraint_name as "constraintName", + table_constraints.table_schema as "schemaName", + table_constraints.table_name as "pureName", + key_column_usage.column_name as "columnName" +from information_schema.table_constraints +inner join information_schema.key_column_usage on table_constraints.table_name = key_column_usage.table_name and table_constraints.constraint_name = key_column_usage.constraint_name +where + table_constraints.table_schema <> 'information_schema' + and table_constraints.table_schema <> 'pg_catalog' + and table_constraints.table_schema !~ '^pg_toast' + and table_constraints.constraint_type = 'PRIMARY KEY' + and 'tables:' || table_constraints.table_schema || '.' || table_constraints.table_name =OBJECT_ID_CONDITION +order by key_column_usage.ordinal_position +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/routineModifications.js b/plugins/dbgate-plugin-postgres/src/backend/sql/routineModifications.js new file mode 100644 index 000000000..879a1195c --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/routineModifications.js @@ -0,0 +1,10 @@ +module.exports = ` +select + routine_name as "pureName", + routine_schema as "schemaName", + md5(routine_definition) as "hashCode", + routine_type as "objectType" +from + information_schema.routines where routine_schema != 'information_schema' and routine_schema != 'pg_catalog' + and routine_type in ('PROCEDURE', 'FUNCTION') +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/routines.js b/plugins/dbgate-plugin-postgres/src/backend/sql/routines.js new file mode 100644 index 000000000..80c189604 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/routines.js @@ -0,0 +1,15 @@ +module.exports = ` +select + routine_name as "pureName", + routine_schema as "schemaName", + routine_definition as "createSql", + md5(routine_definition) as "hashCode", + routine_type as "objectType" +from + information_schema.routines where routine_schema != 'information_schema' and routine_schema != 'pg_catalog' + and ( + (routine_type = 'PROCEDURE' and ('procedures:' || routine_schema || '.' || routine_schema) =OBJECT_ID_CONDITION) + or + (routine_type = 'FUNCTION' and ('functions:' || routine_schema || '.' || routine_schema) =OBJECT_ID_CONDITION) + ) +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/tableModifications.js b/plugins/dbgate-plugin-postgres/src/backend/sql/tableModifications.js new file mode 100644 index 000000000..b34bf292b --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/tableModifications.js @@ -0,0 +1,52 @@ +module.exports = ` +with pkey as +( + select cc.conrelid, format(E'create constraint %I primary key(%s);\\n', cc.conname, + string_agg(a.attname, ', ' + order by array_position(cc.conkey, a.attnum))) pkey + from pg_catalog.pg_constraint cc + join pg_catalog.pg_class c on c.oid = cc.conrelid + join pg_catalog.pg_attribute a on a.attrelid = cc.conrelid + and a.attnum = any(cc.conkey) + where cc.contype = 'p' + group by cc.conrelid, cc.conname +) + + +SELECT oid as "objectId", nspname as "schemaName", relname as "pureName", + md5('CREATE TABLE ' || nspname || '.' || relname || E'\\n(\\n' || + array_to_string( + array_agg( + ' ' || column_name || ' ' || type || ' '|| not_null + ) + , E',\\n' + ) || E'\\n);\\n' || coalesce((select pkey from pkey where pkey.conrelid = oid),'NO_PK')) as "hashCode" +from +( + SELECT + c.relname, a.attname AS column_name, c.oid, + n.nspname, + pg_catalog.format_type(a.atttypid, a.atttypmod) as type, + case + when a.attnotnull + then 'NOT NULL' + else 'NULL' + END as not_null + FROM pg_class c, + pg_namespace n, + pg_attribute a, + pg_type t + + WHERE c.relkind = 'r' + AND a.attnum > 0 + AND a.attrelid = c.oid + AND a.atttypid = t.oid + AND n.oid = c.relnamespace + AND n.nspname <> 'pg_catalog' + AND n.nspname <> 'information_schema' + AND n.nspname !~ '^pg_toast' + ORDER BY a.attnum +) as tabledefinition +where ('tables:' || nspname || '.' || relname) =OBJECT_ID_CONDITION +group by relname, nspname, oid +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/viewModifications.js b/plugins/dbgate-plugin-postgres/src/backend/sql/viewModifications.js new file mode 100644 index 000000000..a74c01a3f --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/viewModifications.js @@ -0,0 +1,8 @@ +module.exports = ` +select + table_name as "pureName", + table_schema as "schemaName", + md5(view_definition) as "hashCode" +from + information_schema.views where table_schema != 'information_schema' and table_schema != 'pg_catalog' +`; diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/views.js b/plugins/dbgate-plugin-postgres/src/backend/sql/views.js new file mode 100644 index 000000000..8d18ee557 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/backend/sql/views.js @@ -0,0 +1,11 @@ +module.exports = ` +select + table_name as "pureName", + table_schema as "schemaName", + view_definition as "createSql", + md5(view_definition) as "hashCode" +from + information_schema.views +where table_schema != 'information_schema' and table_schema != 'pg_catalog' + and ('views:' || table_schema || '.' || table_name) =OBJECT_ID_CONDITION +`; diff --git a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js new file mode 100644 index 000000000..fb86e4c42 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js @@ -0,0 +1,66 @@ +const { SqlDumper } = require('dbgate-tools'); + +class Dumper extends SqlDumper { + /** @param type {import('dbgate-types').TransformType} */ + transform(type, dumpExpr) { + switch (type) { + case 'GROUP:YEAR': + case 'YEAR': + this.put('^extract(^year ^from %c)', dumpExpr); + break; + case 'MONTH': + this.put('^extract(^month ^from %c)', dumpExpr); + break; + case 'DAY': + this.put('^extract(^day ^from %c)', dumpExpr); + break; + case 'GROUP:MONTH': + this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM'); + break; + case 'GROUP:DAY': + this.put("^to_char(%c, '%s')", dumpExpr, 'YYYY-MM-DD'); + break; + default: + dumpExpr(); + break; + } + } + + dropRecreatedTempTable(tmptable) { + this.putCmd('^drop ^table %i ^cascade', tmptable); + } + + renameTable(obj, newname) { + this.putCmd('^alter ^table %f ^rename ^to %i', obj, newname); + } + + renameColumn(column, newcol) { + this.putCmd('^alter ^table %f ^rename ^column %i ^to %i', column, column.columnName, newcol); + } + + dropTable(obj, options = {}) { + this.put('^drop ^table'); + if (options.testIfExists) this.put(' ^if ^exists'); + this.put(' %f', obj.FullName); + this.endCommand(); + } + + //public override void CreateIndex(IndexInfo ix) + //{ + //} + + enableConstraints(table, enabled) { + this.putCmd('^alter ^table %f %k ^trigger ^all', table, enabled ? 'enable' : 'disable'); + } + + columnDefinition(col, options) { + const { autoIncrement } = options || {}; + if (col.autoIncrement) { + this.put('^serial'); + return; + } + super.columnDefinition(col, options); + } +} + +module.exports = Dumper; diff --git a/plugins/dbgate-plugin-postgres/src/frontend/driver.js b/plugins/dbgate-plugin-postgres/src/frontend/driver.js new file mode 100644 index 000000000..425f6a6a6 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/frontend/driver.js @@ -0,0 +1,27 @@ +const { driverBase } = require('dbgate-tools'); +const Dumper = require('./Dumper'); + +/** @type {import('dbgate-types').SqlDialect} */ +const dialect = { + rangeSelect: true, + // stringEscapeChar: '\\', + stringEscapeChar: "'", + fallbackDataType: 'varchar', + anonymousPrimaryKey: true, + enableConstraintsPerTable: true, + quoteIdentifier(s) { + return '"' + s + '"'; + }, +}; + +/** @type {import('dbgate-types').EngineDriver} */ +const driver = { + ...driverBase, + dumperClass: Dumper, + dialect, + engine: 'postgres@dbgate-plugin-postgres', + title: 'Postgre SQL', + defaultPort: 5432, +}; + +module.exports = driver; diff --git a/plugins/dbgate-plugin-postgres/src/frontend/index.js b/plugins/dbgate-plugin-postgres/src/frontend/index.js new file mode 100644 index 000000000..c7606431f --- /dev/null +++ b/plugins/dbgate-plugin-postgres/src/frontend/index.js @@ -0,0 +1,6 @@ +import driver from './driver'; + +export default { + packageName: 'dbgate-plugin-postgres', + driver, +}; diff --git a/plugins/dbgate-plugin-postgres/test/testdb.sql b/plugins/dbgate-plugin-postgres/test/testdb.sql new file mode 100644 index 000000000..ccd36c610 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/test/testdb.sql @@ -0,0 +1,23 @@ +DROP TABLE IF EXISTS "ValuesTest"; +DROP TABLE IF EXISTS "ImageTest"; + +CREATE TABLE "ValuesTest" +( + "ID" SERIAL NOT NULL PRIMARY KEY, + "col_nvarchar" NVARCHAR(160), + "col_int" INT(160), + "col_numeric" NUMERIC(10,2), + "col_bool" BOOLEAN +); + +CREATE TABLE "ImageTest" +( + "ID" SERIAL NOT NULL PRIMARY KEY, + "col_image" bytea +); + +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value 0', 0, 0, 0); +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value 1', 1, 1, 1); +INSERT INTO ValuesTest (col_nvarchar, col_int, col_numeric, col_bool, col_bit) VALUES ('value any', 1241, 14.56, 1); + +INSERT INTO ImageTest (col_image) VALUES (X'FFD8FFE000104A46494600010200006400640000FFEC00114475636B7900010004000000500000FFEE002641646F62650064C0000000010300150403060A0D00000A63000010E8000019F4000027A4FFDB0084000202020202020202020203020202030403020203040504040404040506050505050505060607070807070609090A0A09090C0C0C0C0C0C0C0C0C0C0C0C0C0C0C01030303050405090606090D0B090B0D0F0E0E0E0E0F0F0C0C0C0C0C0F0F0C0C0C0C0C0C0F0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC2001108006E00AF03011100021101031101FFC400E800000104030101000000000000000000000503040607000208010901000203010101000000000000000000000203000104050607100001040103030401030403000000000001000203041110120520211331221406303223154133250724341611000200030505040704080701000000000102001103213141120451712232131061810591A1425262231420C1723330F0B1E18292B234D1F1A2C2D24393241200000503040301000000000000000000003001112120403110501222410232611301000202020103030501010000000000010011213141516110718191A1B120F0C1D1E130F1FFDA000C03010002110311000001EC3E66DE66CE0DDB4FD476C69EAF0BA3DC4DAF2FD20EB7CCD681E948BADB53974AC46A2178F553DE7280A568326F76E124494661F4B40EB6F59C9135685C38755FABA9C89CDF6B267E0ED1DDE21CDAF2E46C4A38FD872EE16AD54279AB85E414CAC7B2DC09375DAB54B507D17F65C6C9324F6E654C9324C93C93DB952D3646DD3E4A6F194871F455FCB3D2AC5558049B308B10BE88FA3BEC39046EB24C9324C9324C93248E2DF5BD6C96E958D160807263548F1DB5CE178851B796A9D081E615C19FE86FB4875A87760A90FB2649A0DB51646336BA8B27509EFC8C0CA5ED134684408625B0CCCF10323C12A5C7511C182D6E6F57A4BDAF375022D624AC5C4A6C251D53E148D7071D0B38263D0E72F6E91C3E77F3DEBA0187A8396FF4E1C359C66784EBF1B30E4A0C73D9D1FEFF00931A1B182D6744344C7D10D13640C771920808ED45A4EAE69F29EE2969DE1AF3B08B14753A0711CA495B1F93B0F8DC4268BE90F79C58C8D85AB130C551B084D166929C621CA1D9E37A6F6E2FA2E6DE6FB001A9C91510B5D9ECC751AB6958B611135C7E1EC0E5BFAAFE85E5A309B0B0C44B154C428DB81E0B0D18DC5D0E7560BD7CEFE57DFD757DE62DAF60EB77BDAF7259334AE6A885653290FA7FEB3E62090C4C24696D4288229A285A9D11221B177E11EBDB42E0F4143F27D88CBD09332129180B5A1A3094848909BB067D5AF5DF1C6EB81E16956DA44EAE038F43923936851460AD2A87E57AA129D754A3A8C036B24696A7684192B33F869416134D3C6EDCF45E14C02E1C6C36228541F4CF60C629E56A3D91894E6AE5FA5439FDAD964BB89D184685CEAE8F9E71CB60CCDA0DE9E6F64FA4F9F0C85BDD28337AA670811316A882DAE2E87DDD358BA94EE0EFADCCEDA39DCAB45D315E4A484DB2D8E4D72DEEF98ED4EBF8B1D716905AD8B4ACB85EC01939094981B2BAE47E37A28F67DC217D68A61EE3B30D2EBCBA56EB6926FB3892EDDC3FFFDA0008010100010502E4EBBA4AF3C4D134A3626F1B7AD55FB3F3527D6380E4677D2ADF58E767FADDE8E46CACD398693C7F2BF5BE339F15DB38ADCB1CC1363717273C806D809B36147C86145CC4DB9DCBE64C052D0A9391C5D04D63183ECDF5D83EC9C73FFD65F63649C7FF00ABB90F99144D863D39899B5B89E3237C3C6FA2BB039E2CE4344B95238BDAF39779D4927664CF0BCCD7B3F173C7F92B01F95929F1EF5CD52304AFA16A479E3EE84FA3794ADB109F2E136505794E20B50D8FC36ED3C36092BC6985AE6CF7638899EE58069CE5BCAF1B2432399B53DA9966D5722F0B0AECD523745565F8FE786491B295BDBD05C0274ED0AE723142DBBCABAC9A6CDD0FCF99D3C14618DBB8045CC538E3E512D0FAD4A9FC07012A9FE94D9472FC1DDE0A1E2299711186B677E248EFCB126F30D4DE56AAFE56B27F2F50297EC15D8ACFD8A7914966494C5DCDBE44F1BC4F154BE1D7E4AFFC282FFDA2E6E9B9FBB223C95B726F313B1B5EF58B0D8F92E4ABAFB0F3373937556889BB9591FB8F28BD3A44E94A7CA9D22DC98A11DF903E4E545A6D6ADCCF2D35974B2B9C557AF0CD44B1D2337CB5DF07312B572AE8A49602D737B2B3FDC913D3D39393820144D55DAE0B9D7FC4E67EC1C8358DB5397B89D2AD975499F4E1B1C7EFCBFE47B8B7CC38BE4C2F3B0AB2C7191E14813DA56C28B11620C5137BB61AB141F68A8DB15B969E574AF39450EE3D56E7B14514B3282FCF564E479417220F21CDE4AEB5A62B4D5F24B5D6A932588420835D8A588B1CE8D062635548248E1E4A2F2D5E4F8E6D98ECD596BBD9812BE230D9B354D4B73C12577C67F7266491B9CE696B46508C95E43E4B75C491536CAC5256638FC5187578653244637C553728A0898BDF23DCD0E6D6B31BE4B35DA55CE132FBBC7D835F95DD2436EF41721A0DACE2E8A32BE0BEDB7F847EE8B898990CAC21EC76F6C9BD93319BD9FA4420992591813F3BA26FB429243BBEC7567A9760E48588FDAE921E56E51966E5AB4C1DFF009F72AF3F0555E79DAEA6E77909C44EF73642E6D86FEE47278DD6A3DE22F68DBB8C8CDED7537B5C611B236E0CFDA3970D64DCDC6FB17EAFF1B71B3EE06C32478DA578EB95E2AA9CC856435424CB21D8A6FD6EC23E877E5BE74376FEDF1D9E9EC526C530ADE1E636FC993FEAC5B97FC8CE7945FE517F9343F9444DFC70DA7FFFDA00080102000105027B501DC85E3241F63507ED39D4A2DCE93FA3B5C223404ADE74C2C68F6EE4617042158D4EAE190F1AEED31A63F3CCC458578CAD851042CACADC3F1176130E99D0B72A48F0B6E81C420FCA700B0B2B3D594E916729BE9958D328BDAB31ADB1946B829F096A7232649D3795BD7917911951712B1A37D1A148FDA1F69C512E2B0B6ADA50DC107922670EAC2C2C6A11F42EC09642F2D1A6DD30B2BFA4C3BA3D47560EF22B32A637A0B7B2C2F453316111F80040613CA95843C74808144E85835731616163A1A148DECE6870734B50EB3D18585844656106203A09C2D80A2D3A1282CE9B50C7E495B82E4C3DFCA42F205B9AB73579423315940E47E3749DDE308B70B72CAECBB2256534171DBD87E1EDA58FD43D0AECBDABDABDABDABDAA1C69FFDA00080103000105020ED72A9D7F9123C6C6CF4D92B1C3074614C94847D51EAC2C69959D2A5A3039BCA4443F956E1CEDC741AB7A31F9DA350EC7463528FE26B328B71A06656D016421A616D0B6A965C20C2B6A2D58E9DA99120CC27A6B517E98418E5E37AC3D79084E9BB451AC6BB178D78D788A1121181A15EA5EA18B798E9B536B342F105E208C61181855F8430B074E565656752A319219B9D5E00D40684F75EA8B17211F61F806AF2A12A9C49ADD5C32B3DD654AC0F6EC20EDEB1A651254632A9B83988A1AB8A2347B37AF169840E9959E82EC985D8746E31BA199B202B2B3900A27567747034051D32B38595B913A8F468CA6592D4D91B96A6B709C1611700BCF84EDC5DD27AAABFDAD6A93F4981AE02B90836508B252BE33936AB023D93863AC159E88E3F6C32640F70D8B0565CB2E432B6A79D80BFBFE1EEBBAA9FA7FAB33D76B3A7FFFDA0008010202063F02BC9DE6445A458C55218D7B66B6936099136AC2475B8C0C0C0C1FC54A6B7FFFDA0008010302063F02A27081BD507E86A1BC1EFA750FBCB20731D6B9D1ED518E62114E7219436C6E822D7920741DC669C8EA1D7263510AA83E87D09F65D64CE49929EDFF00FFDA0008010101063F02AF213F96D60DD19189A672D931057A641F695AEDE0C1A1A5A072E79A33599B74E293E9D65ADD44A969F35A158DAC4EE851A14CD57CCF4DD4D66A5AD2E6A4F3485C251A02287D3E93CC55535D41ADCC0192D559F1084A88668E26A7B754EA26F469B544DEA2714BCD943E9FCD3A4B5343AD46E5696649A34D48F08A0BAA75ABAA14D46A2A2890679711037C0F81E09ECB3085CF5002E6437EC89E689030C0A4917DAB2DDD0B3E5174EF9F666A9411985B3947F6A965B2944954281808FA3A953A1569B7534D5EFCADDE364044D4E9AA209F49C3B593EE2B6452ADE6DE614DE8AB035169166632C26D284A4962531251DBE65A87E5A3A5AAC7C14C79751A963D2D352471DE1003D9552CC847CB18CE1E5788BEE82B32B3F68586118DB92D5EE3B62D11C17C4E766C31D49F0ADA0E167E8E8793536969A9BA6A7CE1FE1539A9D1FE32267BB7F6EC30C40E0ADCBBF18CF4B4954CC5AD90CA38A905DEE9FF002832D3B34BDD21BFA498F9945D06D6523B2D1197D8C441E9939979E9BA9471BD5A47F42C9A66556F6F52DCA9BB69FD7BA1A9D207206CCD50DACECDCCEC71262686621917E6D65136A6BECF7B9B946F8F92B9A78A9E9D3FFD08CC7F844716A7A350F3350591FE66CCD07AAEF59BD977626712EC26857A94BF0311046B34E959AF1593E5D4F12A247C4422E89EA5466E7A751790EC983C5EA8AB51E7D52A4AA6339471F0554E47FDF16F177C5F2FB1699459C507AD5028F705F19138688B962A36218454D0689A597FBCD58FFAFE04F8CFAA1465B14E614C998CDEF19F337798B4C5AE137C65D45547118EF5631C1A8A94E7DF04E87CC15FE178A9A9D52008CD955B34C16C251F52F6BBDAB046D129C3458D31B23E62C620C7E718B6A93072A13B208A47A63BA26ED32604546A5FDE6A9FA3A41F111CDE114A9F33CF3D56C59CDE6198101B02DB60CAB112D96448EA180F447E7B7A6245986C9E305A95504ADEA6C3135CFDF94CE3CBFCBEAD46E9062EC97714003012EC6FB17FD911E5FA76E5D321AD23F17F943EA585A01C820BD47B4F2AE0076BD6A673550B6A2CEC6EFB63A62B639A4C25EBB62F34DC6C895651506DB8C7966AD4D932BE9BA04BC7B1BF42CCAB99C0F96BDF1A4AAF757D2B229C33A5A2134E9CAA8334A0F6AD45B56EA94F065D915ABE92A75594F569627BD0C677E3B78BBE32F4688960CB15180C8AA2765CA677C74EAD8EBC2EB19834C6D86E13E8EDBA2EFB0A2E04DA611688E2B0B3FBDE31A632E2A55785BD7F744EA5E40FB1283958A871C5237C37490D428B9980BE0D4A8895E763AD6133677DF6453A54687D2A4CB5645B98E18080F94671EDDB05455B0DE2422A4D81A865282B5E92D495F985B1F53A31F8E8FF00845A2DC62E8CA7C3EC67AC72AB7E521BF7C3FC0438F08B049AF53DF192AACB61C0EE84CDCB98669EC834AA83256B7BC40A2E66848937C2D069D4B089C2DAC0319365BE46F808C99325CA409C8DA337EF85013295E669CE7D8632B5BB218E5F98A384E313B95B082C2C0701072FA4C0A4E264581B6435337A9946673946CC604904F69B626E730864373020F8C56F2CD59C95A9B1142B9BACDB0F475148348F123081F466D7325A2C71D818FDF0AF5F4EF4355A4196B071CCA310718F2FD4133CF472B6F1099F4D2D6280A6B86B0F843AD5D2D5D5B9E5A74C7EA60E97E9D69D336297CD5186DCAC16174B4F474D0ACB3EB29A939945D9719ED8BBA482C0A6D7F1C22A2E2C84587F69869C03E9870703C3BB081BA0CF08CDB2F86C85733893D93F5F603D924C2F3035817E4D694AA0F785E0C04A9CE97137CA16466030FDB1528AD77E95362A00D80C7FF479669758CD633BD255697E2494717902A9F82BD61FEE8EAD0F2529545CDF5157D6271C3E55A7B394BA9A9FD64C65150504F769009FB2335462619828016F261A24794C4C732C2CF011286A23E58C76C364E24494DF7C4B1DBD83DEA8722FA270CE7D81387F2EF33A2ADA1AEB26AA27359E319695515E81E3D36A05A194EE89F75B05B309B45F16D7FF00498FCF6FE58E0666F0946C85A69693EAEF85A62CA62C9E3BE1A38A5E3D965D1C3F741CDCD8C3FE2E3DD0DEB8B21337BDC1BE462AF55C84CA736EF442DB3E0FBCC0152C59FC86EFC44196CB638413BE2C53EA8B9E3DB8B9E3E72F06315E5F992B7F0F67FFDA0008010103013F21714B51FB3D4C67326CBEF1B83452415903A0AB29BDD2B356B35E770E3E1085A8B4ED46AFC434807986FD79E8B95555BD92F98BBA73E2AAA97DC1F51E4337B47F12D9D3AB40C311ABFBA513C4768E80610AF83E6E79921F6996BB971A6963D449038DACA169F4816CAE6D4FF260994D0E3504A8638AB3D0CE2BA9640195F634458A4B3A8716147CAAB3DC1D6715158BEF30469805460EA466195C168C24A905A92368151BCD8F2B153558BF885FD0F58347ABCA1737C2F9860785AFBE046A08C3BA578B25FCCA5AAB697C47CACBB5C76770D1CCC884B3E8743CE6160DDCD46C963DCA0B6C793F30DFD7E69E4FF9B9E8519C7F7340E0F844B2732CE67668E6553D2CD55F4134CA823F252A7C2257B83344D337EC22740FFE449006A9F012F4C1E7995D0161C9FC54248ABCA4E9000F0D67FE261118B65CD3EDE8E7B67E7325AB4B28CC3D77D303A4A50029626BC85EC3049DA3297591F1BEF287DD7ABE2F4FACBC196D72F0AE2677494DCC09DEC7DC1958A3045DE0BE5DF30CB3E3070561ADEDEC9703A970C0E655D0D0B09666BF22563575DFF507E3E0E3F468DF733932F1AFACECB53E5E18BB34C1CF5739CC07DABFD983EA3156FAB30FD0DDCC4FDAF22E6D724F278A8D3C17A2294AADDEA0903C86A9F0CD951D455B58C157F32808F087DCA835647744D07C7136F659C03957CC283965EE8BF03F130FE412B065E522295FA2A94F0FCB3E8D5B9EF6133C5348CC40965AB301DB3219E1BCE5E0E7DEA166B39E55B47B6292FC6665A0396288D74627EBB65824B603FCA5A5A1CEE64BE20B36707CA2F10A721E63776DDA7C444677F262D3C1708DA294BCFB5FE0955C73D1ED25B2E975965F4841948345AC3F643A7608C550686240DD5C56012D15E3AB2D7CDEBC4D009514DD55D204436356739E254F41FF00F197BF4AEE88FD08CBD97411D23FB3F827283730B89607D05B8AD4E74E0D2F05F8CCB4F5EDC8D7E6ABE6534EDACAA5B2CAF9974DC38B996C6D410E9D1844DDCE3822B726C16BD988212A2704305ED073C40A24117680067B87BD03412B9094B05CA3411607D72E89BCE6291C349F68AF3FA7A8E7D435AD41B68EE72A69E58738D7766804D40CE784AAB3E92C2BCC507459C40500B5C01018A7C18074D6E3A544C800E436E7A9790745671DB1FDAA1912682DD5B1A2007B364FBE6A5330E84CAF6007E6036E1657C7B4B43F0020F1750162D63CA770238161C7BC6F5F23FDC55D22BB1D32CE21DC096524BE33F02099BD43B7FD4BE6CE9E4E4F91E49939B92C876A63F1EE46599524B29BB767B92E149ECAB3EF4C20ED87AB1E3DCA6301425FA7801EE4BA975894D032FED8950FCE726D71A3E3D2088168B62F692A09E2E64DAD1F4CD798237A79BE5C6311485B72C02CBE58E8468DCF2C37C4CE6BDF9AE7E62D50BAE4FEA5F985BD8B3DE535D4E15E7CC117E9D0A84E5D7D64C0FCF716C115C64E7FD817EAC18178B4E7FF0052CE08815ACD4F70CC91882F3DBF23F696BD8E3236B5DB330E2BEC1DA58F8968E698FF0002293811E751D354B91814D1CBF339EE9217072F45EBED2BBE9686CDD16FD2BC4C5CE3F04EE5D7BA5D36AFF7B3160F057DE558F708A1682AF795AC535127C1A651534E4967218E9A97980F90C7E0DED00ABA0B57006B0AD278BC9E38E22D68CDF202FEB2E983ED28AA7091F5717B5F2316759D0FD17284E2998AE4D92532ABB023DE1FDA5AB1C552BE24DAC85DB6AB0E8136378D1E63349D7E09903BFC7994657F70EA6233006BC11043973ED298D837E5E6C8301224C17C261991F7C6F1730155D37CD93F4198214BD3477027488144A5BC0F264DCB6AE6A9D99C2CD3050E0B57930C655B5A0F3F316E55D99FEA7313C12FDBFF00B8032BC581F99CCAF26234272DE9CAF69B93EF90ED799C6DE3F04D35F0C2537C33372E9532BF547E604E057DDC399DAEA936D3E272D5FB07C5C5D94B25E39694028D0CF8C0CCF112799EA7EF89EC56F8453ED423C4FC7FA97FF9BFB9EDFB62E0BF48CCA391AF8E66BD5539EFF7DCCDCFFFDA0008010203013F214CCA3D16008E8A9858DEE4A4BF5ED730824C2150BFA75D421B997713D149504D443132C107EAC65F41823519494CED0952BF557A73FA2B62F8F458F0108210C7FC822CB611ACB589CA343488CE4A0689B1A46EDE8FCCF385A5FE8B23246E5A4C58C10A7A248DDB15F49B2413319CE88BE0F45A4A279CAC4C5E3D38922A8E4837446A366CBF7070F48AD784254951F4A95EBBEBC99840FA6809E95A851E8112ED136F8827FC07D609C5F40F42036F4BCB84A9622FEA7D40188055EA38FA20A811F41F41080CC5545B2A5F72CCFAB69E81E8F5996454BE111AF42A566A2543D70972A25C292929061772DDC125FA76A6A6447F8884327A5917A37A26D98698F4E7D398FAB065FA25BC469ED0E0C0966C33F6D81FFA9D0237C4CA5C56A1E8465C2312557A1297A20AAE25DB217819EE97DA0466B1027E88FA139FD0C4A4764F295E95454D7A1D1E9FFFDA0008010303013F21C5E970804F79E65FF840713123130C565B3D6866659E929A6A07307E83D11E8BF52E50E58EE5B1B3E20551BF31916DF5DBD08B1E84A8454A951FF9D9FA20DCA95E8A95E84113FE2914EA546CB886D7FB1E29484031F49E41156B71EBE85BF4541B118466D05382332A0E700874CA764E0210EB72D6DB95A844195E25A5E10EDF4EC7A130C5C7A3D6D4E1A07C45982D9372432E529257A8C20F4081FD114A901A251E8A67399881751BC17D350F425FEA6395B2DCE507AD08548CD4A7715734125B2117E87A0C3D171710C62D6CCC3C9C6FDE11D1163D1F449949369DC054B2784AF0CBF417972E5CD47A47D7D9DCB47D1C9E92D2F612E3D0DEA370C62DF3125115CBF45BD559000975997A7CD361A660CE3B8FC041B22AF11FDA7699163150B62225F0E883184D4DCD207A54A9505ED20B925EDDC04A667E53269BED260DFB65D0645CFBCA8A258393C423063E9703D47A3B7E12E70C650CC4944A389E296EE1056457F478988C37E998431693AC502D45F12FC4BF12FC4B62C6F51B9FFFDA000C0301000211031100001009CCE8E3F307D1E02D403E0EC85FFD389B20492811FEA747B60000000A04ADFA45A74012BA857F0D683CB8BA68EFCC5D152412FC91D53E7A0ECCDF9C66610B29B3F92E6F081C5E9D2FC044B5ED24511E7B872C983CA1BBF3A258D77550407880D497C65E69C331486C1F53BBEEC7A977DD41756FFFDA0008010103013F10AF3BAC2BCC40BCB84CBCE7D470B25AD801AA235CC8C6B5AA655A8D4966DCD0102433D10F3332E738152A42D4BBB0A8CB3C50CD2AE2ED3E23686675C406CBA1D695016607E8763F497F4F4BC317D49C35BB4C132F2A9B416694A9714F55EF8ADDAA50E23B88054AE01982E00BF1FE9666AC46C7564336042B4F4CA8DF264D02B2D3C1D46AC5C84C0B7540283063DE2AD256C8C0AB33DEEE36B8A4C05442AA2C46CABCC71BA2A8745800380DCAD14294589D54A6C9DB74C74D2B87183908BF9AA0B62C9C706B3A97CCE0B25D5D01CD1034CA875772D4D8D64A4186B7C5E83ADCED5A77A56387A9CFF4BF612156B9422242940E80E00C109CFF0031F4A0A01B60BE5507962116A6EC38DD384812EE5F62475199178426E3118C1ACB7D5F35116F20B6D393B8CA6AD6104C135754F8814B7253891B2A8138BB993052EC36F66BEF1E5052A8BD25D022B5759330CB003928EF7A7B4648DF0A14B4DD598DEA1A254AFD15E95DFA2C09000B98315EA6F4D256A976B7CC5907447818589D55917BA6C141101C2E4F7943105C719C2E959583A2768540720E9127631B3CA915ED3740E15BE930660CA40004A038D930B9AD31A3C2408CA1585E002E292EDB3E78C12F4C9A4E9CA5383FF14C00E42C5882AC5AECB688E569E965AD01ACBEC514439D781ECE11A47DE27C309190D633414D8A2638A287F2DE9D393BD653891AEED15BF69BE88F14C883AB9AE193FF0066A5948DF8C71328198AC22F4F2CA8108E30C6428DA34B6D173EC4894395D6C05642D8C73014E0C559165A65AC13832EAC3A87904AB30F22420E5855423B4295F8A848BD8511F59655DE3D6AB35A00BAEAE04847656163B5FC5CA61D6215B8286F9CE894FB9B3926133F130EAA2BC17F1EE2007A3B442B2D73B443034228D736035F146D757044587542ABE0332BE32F2873485B15BFE4DBDD6F8F98A59AE01EABD94E7A82D3E85C94C675FA44D8E42CAE97039388BDF9D32E9716250F97B8502E928A07409DBF06620E5E3E14E3AFDDCC9B49FC70D79995D08560F68D4A9F73F4BACF881566D8A93BC950DA074D9F985507E08FCC35B8058A1C675D7503A11D65129B5A658F700ADBBCB98D71C10BEECD0098E83F600398754210451D5B651047A0021D0FEE3C06621E105C26ECDD59C0D130CEBE079ABC9AF68584C1D1001F632E91175BD942A1871BE49562E38B8A5253C372B64230855C095531D420893A140A2AD10756CAC5E06F556DFC4B735825544CCF2C11A73A894DB097A3AB6A28ABBAE238732E29954B2EA37C7C67F88880E178317CF010B614A29701BAE6BF11C19A67D3C81A1AAD16CC0052D0B7CEDDB056CAC17C5A317B0B5B3031B4CC01938CCB28A38712A67F8000052B488C7C84060F794CBE9F32BB585ADB3CD580E76C21B8E19C2EF3EF72C70CE2254EF9C88DA55090AE30C438CC4AE630A73348C4A26DB0D715CE416A55064A065669C903C220387B000AEF36E7E222A1B754CB59D57B444AC58C952F008470D9AB30E623061A6B94A1938130360333C0A6540E6D4428AC68D4278B6D4141908366D6AE2714F0D00DB05C295DCB40153C8559193DA612ECA50CAAF550558EE21F111AD0A0D23C32C14C731380F058F2B4F76899B3319B2A103CA1DB7344320A0DD198D96882068DB4529BB28A87F562A5AD42A737C0CCA680EB9A7F0163F1DC75194AFBB1F352E11C8BB67DEA2927D15AAE0023FA06603114033A661A1294219A1180ACE0EAECB382C182DA3E11F215790573306ECCE3A223E661935DE19BE4826DF66D0889C37A090B2D41C0154B699C57DE0AA957ABA1D3DC63C9D8250348E5C3B3E4EA2EBF7B042C3A71EF2EC5274DB3EE3ED0A6CD1CA0D617D9FD404A46D0D432510212235AD9774D7A39DFBD5896E2F91F1663EA984AB5481F08CDE4CE23C71FE398E13BE4D34C6C9500324173D9600015EF456D2F292DC35356156AB01F025F51358ECE897B140F4C51D3026426A16894E3B949102D9A0ACA8F9551C085EF001264AA011A0739BAB949120131EA2E836B5C4B5A0C2D44EF2DB5CCCD3F9B978B90978EE3CB6459C85314DF399822CA005E5ABBE639EF5DE614DE28226B0B14D28C844B4225F106EB2809928039AD885E500CA0CE6DC7D0DC007112E404CABE26E162C9713073835C543EC88F685F66668E1E152FB004AE179C592E0323A855834DD6823D32F88CE85000053811EF7114054BD83CB8691036E750959E9091119767374AA35539393B032A2B2E2F78087278181C4A618A64F578C29284158AE186B42DD394157F448C2EE200DE11B6D528D5E2F04716B345AD691995CF72B0649CF824152E357F8A1A68B30D60E1F6EE01A12EBC960FB7DE7165F4945EA540382F263DE0922A7305130F6FF70BA82038AE45E429311393014FD33F2445722D9ECA4524511E95DA42DE40B38950C58A014CD0A3B35A6ABC14FA9354341D8BB70C4A88012C145041A073097008A1083681A65B38E96CD2ABF58E3BC0D62F80CFA41B9A06B7021BA582DAB242E05E59C436303998D955EC82DCB02DA2C2D6F79CC430C53884B8CB44BC97A85C9D54E03DB8A7E6BF8877D0B136BBF8392509511E4D1B78B9BCCB19C726EA55684D68E15C88C2D552D4946EDBFBEAEE0B05BFED435A99A19E9F88A84252762378A74450172A61752B51EE7FE64046C3BC0942EA559380118156BD32B39A0495556F6DAD1C71D621265B402BDCAABE63789D13ED5FCA00BF320FD6E57C8F855F787C05AB38EF367ED14E41D95F998BD3A0839A9C0B581D90AC6510BC656EAA8D4BF334C95F339FA4BAB97FC21BB989DF2DEDCEB31E1AF5F7EB734372F371F15D54799B4C2B46BB6EAA02D06728305E1179BEA06D57DBFC428B4B617DED7DEFCC36228B64111630670412B416296311C591074D9F09D4B6936B472E232537B5D77DCB6117918BE369F796189C2D15B61ECCAEDC321CB1D2FF0062535D259BFD09DCDFBAAAB4B9878C30F1789D97778F79FFDA0008010203013F10441D09B2355562605A0EF98DCE3F6EE146F365ABB610746C7F304CC3E8F4C2987814EC40D17B978F129583300CB12E9326258DA5F3E8EA381A437300EC259C1001896DE200159DC7A2AC2504C20B750350D4711DC956512A67CFA490998A372A311B65323F4152A25C0A9E5E853487AAA2699454E07C7DC9D7FC4D923E22752CC4CD1CE28F787FC48BE25B4884C42C36C601334F6FF63D957E58C36C26982614431BE4298F2C70CAE3F7EF299E1E7F873316A2A599257F425B609820B4B3D117E75D4A94B9CCFE21025CDDB3000CDC54E3BF7F466E88AF11C70FBC9FC10F298263E60B619430DECC4EC4F0CA78333404F024B9B63065DBD4473E63A5E9032CB3B14194257BC5E9CC5A97E2368BC0F2C168BA890B99BC423094CC4C4C2620B52827022A8AE65C1D4ADDE2090082B7684495532CCF272A3787E8CB6EDA0040C4DA54A8912BD3020CD91198CA622D5FB4A123416EE008151D37150D903144AB9824517DFB7994D5AD8CE719622253E94D5C73895E9A80D5CAC6911E434CEF665767AC1531C7A08EA2992E50815363C8F10235A8D0C44DD0BF42748F84454C4B82EE1E20ECE88BED660A6A79752807CF0C71A40974B95054D6E62381AD10A421551A546166A60120111328D2914EE39C304BD91C30D55E07F89B17CBFB99285625E45735F88035983C8B02E12BDE526BE522DB3A3AE6098314DBCFA55425CA081F4F433C438961083B9D8286539FC251F5194B818FE305457FD23767DE81FF00AA604A1E08A56B1B840DBF11D912E5B984F996AA87312344C706A34CCB708C97371C6D4C31A022CCBBCC214F28070DFA2486A263AAAFACD3D6D79F4B71335BEBD1AE655A78C1477516269C3FBCC05416E595DBF30E47F33C9F9876FE625709F7872E5FC7A7FFDA0008010303013F102C23316A11F0C2458AE9E541E079654D08D0553BFA67AF128B4A28C251A7B3C3ABC6728E3494FC7AE01D301A0E6471CD3C7889B4D5B1E0971941E612AE524B4018A95208828A8A633A0A8765DE3C91603748BF8CB7055E8A2807D97DE6D24B7D7110D41701A9BE62430A95E80FD0C6C7FCEF5BA8FA22CC73613CD253B87942308311405E621FF17F24A8114302345CBFC1CCE97F7FA344672AF3FE46F204A24DF7A0B061BF782EAB9350D0B3DF895159100D6225C7E813A26FB11D10F97F897EDBDC775F13862D1DC49AFDF83A3DA5971B8BC02C6EC879775823291A20DB1500A7A36D16CA8BE9B9E32712A772651CB00189AC738131D12A0AB2F3043F282E0DFB4D30964A23F50D69F485681B7A25AE10875E8BC2B3BE241B8A8970F8608E6770287CF70C4BA800D25D59220789908C45C0FCEA1C4BB34F450A306C81515C19751CA8E600DF224CA0CAE3DA1628C733191C855E18A1F11FFD111FC0C7BF0C34D129FEFE626710109841B6714136F490B8C9619F187C4DC86039205625A24B72DCBB8473529021BBAEA27BB5F7EA16050E6295EFB97399472AA5AFBB3683210B2187A12198341BADC12F38FACDA93E83FB96BB3CAD9E497AB08ABCC08ECA81771FBB96445DA8AB2AB64B815580F2AD07CCE62588103D5CB770159A858DCBC71965583304A9B33F481654429DF997BC2531F59B79C0FF0024CAE0F9CFB435AB98829CB8863083B6618375D626BF1D3C4465472EA521C44725E3EF2C20A60C4729B4CC4B92F47A611E908076FE88CAB4F5C3012AA149032E037AFC4FB0A24FA3308A9E521BA1ED8ECAF9AFC45E8B792FE61A0CE5442BE5858880732AC333835302E7225AF3A8D246AF88B507500EBDB886028D939D10558E222C54702AFCCF07EB07008EE562D709AD88FD1B98807A18E7713BCAD1D4A6B18758B2A8A998ECEAE617CDE498B4627171FF00F52FC0FB7F717D3EA45F4FA90E0227057C22F97FB3FFD900') diff --git a/plugins/dbgate-plugin-postgres/webpack-backend.config.js b/plugins/dbgate-plugin-postgres/webpack-backend.config.js new file mode 100644 index 000000000..fb19d92d1 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/webpack-backend.config.js @@ -0,0 +1,40 @@ +var webpack = require('webpack'); +var path = require('path'); + +var config = { + context: __dirname + '/src/backend', + + entry: { + app: './index.js', + }, + target: 'node', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'backend.js', + libraryTarget: 'commonjs2', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, + + plugins: [ + new webpack.IgnorePlugin({ + checkResource(resource) { + const lazyImports = ['pg-native', 'uws']; + if (!lazyImports.includes(resource)) { + return false; + } + try { + require.resolve(resource); + } catch (err) { + return true; + } + return false; + }, + }), + ], +}; + +module.exports = config; diff --git a/plugins/dbgate-plugin-postgres/webpack-frontend.config.js b/plugins/dbgate-plugin-postgres/webpack-frontend.config.js new file mode 100644 index 000000000..db07de291 --- /dev/null +++ b/plugins/dbgate-plugin-postgres/webpack-frontend.config.js @@ -0,0 +1,24 @@ +var webpack = require("webpack"); +var path = require("path"); + +var config = { + context: __dirname + "/src/frontend", + + entry: { + app: "./index.js", + }, + target: "web", + output: { + path: path.resolve(__dirname, "dist"), + filename: "frontend.js", + libraryTarget: "var", + library: 'plugin', + }, + + // uncomment for disable minimalization + // optimization: { + // minimize: false, + // }, +}; + +module.exports = config; diff --git a/yarn.lock b/yarn.lock index 8f31afac6..330e9962f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,50 @@ dependencies: "@ctrl/tinycolor" "^3.3.1" +"@azure/abort-controller@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.4.tgz#fd3c4d46c8ed67aace42498c8e2270960250eafd" + integrity sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw== + dependencies: + tslib "^2.0.0" + +"@azure/core-auth@^1.1.4": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.3.0.tgz#0d55517cf0650aefe755669aca8a2f3724fcf536" + integrity sha512-kSDSZBL6c0CYdhb+7KuutnKGf2geeT+bCJAgccB0DD7wmNJSsQPcF7TcuoZX83B7VK4tLz/u+8sOO/CnCsYp8A== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.0.0" + +"@azure/ms-rest-azure-env@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz#45809f89763a480924e21d3c620cd40866771625" + integrity sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw== + +"@azure/ms-rest-js@^2.0.4": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.3.0.tgz#035bdd2b0f64de2f1b5f9d42ac146d4afdf54434" + integrity sha512-8NOnHgovi61NpcUld53zRkY/IcQJBBO48VeMntNTUtaPo8yYYTnu1hWRvp6b6vpBnur7HGmuj692J9li5Kx6/Q== + dependencies: + "@azure/core-auth" "^1.1.4" + abort-controller "^3.0.0" + form-data "^2.5.0" + node-fetch "^2.6.0" + tough-cookie "^3.0.1" + tslib "^1.10.0" + tunnel "0.0.6" + uuid "^3.3.2" + xml2js "^0.4.19" + +"@azure/ms-rest-nodeauth@^3.0.6": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.9.tgz#29029f990610286d318ff735323e826a95d7e1af" + integrity sha512-+GdDHUJlWtIDanRZemFooLy68NsBDhN/Oni9DSFeoXIFNGlSe1IOes8/IRkQdrNXyUvBanuzzR7I5WYYzYQsmA== + dependencies: + "@azure/ms-rest-azure-env" "^2.0.0" + "@azure/ms-rest-js" "^2.0.4" + adal-node "^0.1.28" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -330,6 +374,11 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" +"@js-joda/core@^3.1.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + "@mdi/font@^5.9.55": version "5.9.55" resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.9.55.tgz#41acd50b88073ded7095fc3029d8712b6e12f38e" @@ -356,6 +405,65 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@npmcli/ci-detect@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + +"@npmcli/git@^2.0.1": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2" + integrity sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg== + dependencies: + "@npmcli/promise-spawn" "^1.1.0" + lru-cache "^6.0.0" + mkdirp "^1.0.3" + npm-pick-manifest "^6.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.2" + unique-filename "^1.1.1" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.6": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== + dependencies: + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@npmcli/node-gyp@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" + integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== + +"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== + dependencies: + infer-owner "^1.0.4" + +"@npmcli/run-script@^1.8.2": + version "1.8.4" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.4.tgz#03ced92503a6fe948cbc0975ce39210bc5e824d6" + integrity sha512-Yd9HXTtF1JGDXZw0+SOn+mWLYS0e7bHBHVC/2C8yqs4wUrs/k8rwBSinD7rfk+3WG/MFGRZKxjyoD34Pch2E/A== + dependencies: + "@npmcli/node-gyp" "^1.0.2" + "@npmcli/promise-spawn" "^1.3.2" + infer-owner "^1.0.4" + node-gyp "^7.1.0" + read-package-json-fast "^2.0.1" + "@polka/url@^1.0.0-next.9": version "1.0.0-next.11" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" @@ -419,6 +527,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@tsconfig/svelte@^1.0.0": version "1.0.10" resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-1.0.10.tgz#30ec7feeee0bdf38b12a50f0686f8a2e7b6b9dc0" @@ -546,6 +659,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.7.tgz#1628e6461ba8cc9b53196dfeaeec7b07fa6eea99" integrity sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg== +"@types/node@^8.0.47": + version "8.10.66" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== + "@types/parsimmon@^1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.1.tgz#d46015ad91128fce06a1a688ab39a2516507f740" @@ -594,6 +712,11 @@ dependencies: "@types/yargs-parser" "*" +"@verycrazydog/mysql-parser@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@verycrazydog/mysql-parser/-/mysql-parser-1.2.0.tgz#6007a8dc8987c4b7c8fd10357ee52f31df9062bc" + integrity sha512-h3zGaAwDYbZwqEIxDLMGaDRhfznTxgLO9C2FUnAfdXKoaw+OAUkMGbkjs2Qi+pnxhrAOt4BXM9JwkMb6bY/iXA== + "@webassemblyjs/ast@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" @@ -765,6 +888,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.4, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -811,11 +941,58 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== +adal-node@^0.1.28: + version "0.1.28" + resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.1.28.tgz#468c4bb3ebbd96b1270669f4b9cba4e0065ea485" + integrity sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU= + dependencies: + "@types/node" "^8.0.47" + async ">=0.6.0" + date-utils "*" + jws "3.x.x" + request ">= 2.52.0" + underscore ">= 1.3.1" + uuid "^3.1.0" + xmldom ">= 0.1.x" + xpath.js "~1.1.0" + +adler-32@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25" + integrity sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU= + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -841,7 +1018,7 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.12.5: +ajv@^6.12.3, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1035,11 +1212,21 @@ async-lock@^1.2.4: resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.4.tgz#80d0d612383045dd0c30eb5aad08510c1397cb91" integrity sha512-UBQJC2pbeyGutIfYmErGc9RaJYnpZ1FHaxuKwb0ahvGiiCkPUf3p67Io+YLPmmv3RHY+mF6JEtNW8FlHsraAaA== +async-lock@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c" + integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ== + async@0.2.10: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= +async@>=0.6.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + async@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -1205,6 +1392,21 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" + integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bl@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f" + integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ== + dependencies: + readable-stream "^3.0.1" + bl@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -1379,11 +1581,26 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +bson@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a" + integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg== + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer-writer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" + integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1423,6 +1640,11 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + busboy@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" @@ -1461,6 +1683,29 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^15.0.5: + version "15.0.6" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" + integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1513,6 +1758,15 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +cfb@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/cfb/-/cfb-1.2.0.tgz#6a4d0872b525ed60349e1ef51fb4b0bf73eca9a8" + integrity sha512-sXMvHsKCICVR3Naq+J556K+ExBo9n50iKl6LGarlnvuA2035uMlGA/qVrc0wQtow5P1vJEw9UyrKLCbtIKz+TQ== + dependencies: + adler-32 "~1.2.0" + crc-32 "~1.2.0" + printj "~1.1.2" + chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1660,6 +1914,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -1696,6 +1955,14 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codepage@~1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/codepage/-/codepage-1.14.0.tgz#8cbe25481323559d7d307571b0fff91e7a1d2f99" + integrity sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k= + dependencies: + commander "~2.14.1" + exit-on-epipe "~1.0.1" + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1750,6 +2017,16 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@~2.14.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw== + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1918,6 +2195,14 @@ cors@^2.8.5: object-assign "^4" vary "^1" +crc-32@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -2042,6 +2327,31 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csv-generate@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.0.tgz#360ed73ef8ec7119515a47c3bd5970ac4b988f00" + integrity sha512-D6yi7c6lL70cpTx3TQIVWKrfxuLiKa0pBizu0zi7fSRXlhmE7u674gk9k1IjCEnxKq2t6xzbXnxcOmSdBbE8vQ== + +csv-parse@^4.15.3: + version "4.15.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.15.3.tgz#8a62759617a920c328cb31c351b05053b8f92b10" + integrity sha512-jlTqDvLdHnYMSr08ynNfk4IAUSJgJjTKy2U5CQBSu4cN9vQOJonLVZP4Qo4gKKrIgIQ5dr07UwOJdi+lRqT12w== + +csv-stringify@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.2.tgz#e653783e2189c4c797fbb12abf7f4943c787caa9" + integrity sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A== + +csv@^5.3.2: + version "5.5.0" + resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.0.tgz#8ef89e9ac22559064aedf3cbbb912ed4c2aaf9ac" + integrity sha512-32tcuxdb4HW3zbk8NBcVQb8/7xuJB5sv+q4BuQ6++E/K6JvHvWoCHcGzB5Au95vVikNH4ztE0XNC/Bws950cfA== + dependencies: + csv-generate "^3.4.0" + csv-parse "^4.15.3" + csv-stringify "^5.6.2" + stream-transform "^2.1.0" + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -2068,6 +2378,27 @@ date-fns@^2.0.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.9.0.tgz#d0b175a5c37ed5f17b97e2272bbc1fa5aec677d2" integrity sha512-khbFLu/MlzLjEzy9Gh8oY1hNt/Dvxw3J6Rbc28cVoYWQaC1S3YI4xwkF9ZWcjDLscbZlY9hISMr66RFzZagLsA== +date-utils@*: + version "1.2.21" + resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64" + integrity sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q= + +dbgate-plugin-tools@^1.0.0, dbgate-plugin-tools@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dbgate-plugin-tools/-/dbgate-plugin-tools-1.0.4.tgz#ab89920126f397550a7e79c5f09fa568f1b83b14" + integrity sha512-SWtN2KyptwvMAaoCyaLqvnraBranfd925exdub5Rx9bsPGYQ5v+Hx3v4SWYvK4k7qpqyJglxXaRN8RIMPN6Apg== + dependencies: + ncp "^2.0.0" + pacote "^11.1.13" + rimraf "^3.0.2" + +dbgate-tools@^4.0.3-rc.1, dbgate-tools@^4.1.0-rc.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/dbgate-tools/-/dbgate-tools-4.1.0.tgz#cacc62759fb43a4c2a54d5d0ff795f2f7995a445" + integrity sha512-IvvTOvGezUyiz9NP6GnheOU8rO8McQri2BSPBVv/p5MV0+veDLWo16Ag/RqIoMlTcmV/AZz5orwcKd5BLpGaaQ== + dependencies: + lodash "^4.17.15" + debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2075,6 +2406,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" +debug@4: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2167,11 +2505,21 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: +denque@^1.4.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" + integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== + +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -2287,6 +2635,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2330,6 +2685,13 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -2403,6 +2765,16 @@ env-cmd@^10.1.0: commander "^4.0.0" cross-spawn "^7.0.0" +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -2594,6 +2966,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" @@ -2638,6 +3015,11 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2824,6 +3206,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fflate@^0.3.8: + version "0.3.11" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.3.11.tgz#2c440d7180fdeb819e64898d8858af327b042a5d" + integrity sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A== + figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -2963,6 +3350,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -2977,6 +3373,11 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +frac@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b" + integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -3029,7 +3430,7 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -3093,6 +3494,13 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -3262,6 +3670,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.2.3: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -3280,6 +3693,14 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + has-binary2@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" @@ -3387,6 +3808,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== +hosted-git-info@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + dependencies: + lru-cache "^6.0.0" + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -3399,6 +3827,11 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== +http-cache-semantics@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -3421,6 +3854,15 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3440,6 +3882,21 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -3447,6 +3904,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -3467,6 +3931,13 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= +ignore-walk@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -3516,12 +3987,17 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= -infer-owner@^1.0.3: +infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== @@ -3590,6 +4066,16 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + ipaddr.js@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" @@ -3757,6 +4243,11 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -3808,6 +4299,16 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= + is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" @@ -4332,6 +4833,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsbi@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" + integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -4379,6 +4885,11 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -4439,6 +4950,11 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -4449,6 +4965,23 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@3.x.x: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -4619,6 +5152,16 @@ lodash@^4.16.0, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.20: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4631,7 +5174,7 @@ lowercase-keys@^1.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== -lru-cache@^4.0.1: +lru-cache@^4.0.1, lru-cache@^4.1.3: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -4646,6 +5189,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -4673,6 +5223,27 @@ make-error@1.x: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +make-fetch-happen@^8.0.9: + version "8.0.14" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.0.5" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + promise-retry "^2.0.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -4748,6 +5319,11 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +memory-pager@^1.0.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg== + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -4877,6 +5453,53 @@ minimist@^1.2.3, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" + integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" @@ -4884,7 +5507,14 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minizlib@^2.1.1: +minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -4916,6 +5546,11 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mixme@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.0.tgz#7a7ebbb51251724b18054c63c91f5487c0b030ab" + integrity sha512-YyyBIzqe6EEi5xcnN66LXVVvwijMF51liPT9ZqsrHim9s2MgEg4jxI8gsSF6R7pzAotjvBiERC90bbnwAqiDHg== + mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -4935,7 +5570,7 @@ mkdirp@^0.5.5: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -4950,6 +5585,19 @@ moment@^2.24.0: resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== +mongodb@^3.6.5: + version "3.6.6" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.6.tgz#92e3658f45424c34add3003e3046c1535c534449" + integrity sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w== + dependencies: + bl "^2.2.1" + bson "^1.1.4" + denque "^1.4.1" + optional-require "^1.0.2" + safe-buffer "^5.1.2" + optionalDependencies: + saslprep "^1.0.0" + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -4977,11 +5625,16 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + msnodesqlv8@^2.0.10: version "2.0.10" resolved "https://registry.yarnpkg.com/msnodesqlv8/-/msnodesqlv8-2.0.10.tgz#47d05157c2d30e5f48fa0aa550a0353ab56a027c" @@ -4995,6 +5648,27 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mysql2@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.2.5.tgz#72624ffb4816f80f96b9c97fedd8c00935f9f340" + integrity sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g== + dependencies: + denque "^1.4.1" + generate-function "^2.3.1" + iconv-lite "^0.6.2" + long "^4.0.0" + lru-cache "^6.0.0" + named-placeholders "^1.1.2" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + +named-placeholders@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8" + integrity sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA== + dependencies: + lru-cache "^4.1.3" + nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -5027,6 +5701,11 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +native-duplexpair@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz#7899078e64bf3c8a3d732601b3d40ff05db58fa0" + integrity sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A= + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -5085,11 +5764,32 @@ node-cron@^2.0.3: opencollective-postinstall "^2.0.0" tz-offset "0.0.1" +node-fetch@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-gyp-build@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== +node-gyp@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5185,6 +5885,13 @@ noop-logger@^0.1.1: resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -5214,6 +5921,68 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-bundled@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-install-checks@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== + dependencies: + semver "^7.1.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.2.tgz#b868016ae7de5619e729993fbd8d11dc3c52ab62" + integrity sha512-6Eem455JsSMJY6Kpd3EyWE+n5hC+g9bSyHr9K9U2zqZb7+02+hObQ2c0+8iDk/mNF+8r1MhY44WypKJAkySIYA== + dependencies: + hosted-git-info "^4.0.1" + semver "^7.3.4" + validate-npm-package-name "^3.0.0" + +npm-packlist@^2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.5.tgz#43ef5bbb9f59b7c0ef91e0905f1dd707b4cfb33c" + integrity sha512-KCfK3Vi2F+PH1klYauoQzg81GQ8/GGjQRKYY6tRnpQUPKTs/1gBZSRWtTEd7jGdSn1LZL7gpAmJT+BcS55k2XQ== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +npm-pick-manifest@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== + dependencies: + npm-install-checks "^4.0.0" + npm-normalize-package-bin "^1.0.1" + npm-package-arg "^8.1.2" + semver "^7.3.4" + +npm-registry-fetch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== + dependencies: + "@npmcli/ci-detect" "^1.0.0" + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -5221,7 +5990,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.1: +npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -5333,6 +6102,11 @@ opencollective-postinstall@^2.0.0: resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== +optional-require@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.0.3.tgz#275b8e9df1dc6a17ad155369c2422a440f89cb07" + integrity sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA== + optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -5405,6 +6179,13 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" @@ -5425,6 +6206,36 @@ package-json@^4.0.0: registry-url "^3.0.3" semver "^5.1.0" +packet-reader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" + integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + +pacote@^11.1.13: + version "11.3.1" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.1.tgz#6ce95dd230db475cbd8789fd1f986bec51b4bf7c" + integrity sha512-TymtwoAG12cczsJIrwI/euOQKtjrQHlD0k0oyt9QSmZGpqa+KdlxKdWR/YUjYizkixaVyztxt/Wsfo8bL3A6Fg== + dependencies: + "@npmcli/git" "^2.0.1" + "@npmcli/installed-package-contents" "^1.0.6" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.8.2" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^9.0.0" + promise-retry "^2.0.1" + read-package-json-fast "^2.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.1.0" + pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -5596,6 +6407,70 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +pg-connection-string@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= + +pg-cursor@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/pg-cursor/-/pg-cursor-2.5.2.tgz#9217fc989fa64221a02d6ed4b37323267d90abde" + integrity sha512-yS0lxXA5WoIVK7BUgJr1uOJDJe5JxVezItTLvqnTXj6bF3di4UtQOrPx8RW3GpFmom2NTQfpEc2N6vvdpopQSw== + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-packet-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914" + integrity sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg== + +pg-pool@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a" + integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg== + +pg-query-stream@^3.1.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/pg-query-stream/-/pg-query-stream-3.4.2.tgz#6cdf8f3086bfe01b2e4d7cc461ad4c8c6a7d0914" + integrity sha512-kaTzsi5TQ3XG1KUznEV3MnstM1U4k5Z9cZ02PNmKLMFeYiPEn83FUc2pPVPiKKv93ITI8e5oCh+zEOunjy0ZwQ== + dependencies: + pg-cursor "^2.5.1" + +pg-types@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@^7.17.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.18.2.tgz#4e219f05a00aff4db6aab1ba02f28ffa4513b0bb" + integrity sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "0.1.3" + pg-packet-stream "^1.1.0" + pg-pool "^2.0.10" + pg-types "^2.1.0" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.4.tgz#85eb93a83800b20f8057a2b029bf05abaf94ea9c" + integrity sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w== + dependencies: + split2 "^3.1.1" + picomatch@^2.0.4, picomatch@^2.0.7: version "2.2.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" @@ -5649,6 +6524,28 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + prebuild-install@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316" @@ -5705,6 +6602,11 @@ pretty-format@^25.1.0: ansi-styles "^4.0.0" react-is "^16.12.0" +printj@~1.1.0, printj@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -5725,6 +6627,14 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + prompts@^2.0.1: version "2.3.0" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.0.tgz#a444e968fa4cc7e86689a74050685ac8006c4cc4" @@ -5893,6 +6803,14 @@ react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== +read-package-json-fast@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz#2dcb24d9e8dd50fb322042c8c35a954e6cc7ac9e" + integrity sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -5919,7 +6837,7 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -5932,19 +6850,19 @@ read-pkg@^4.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606" - integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA== +readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +readable-stream@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606" + integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -6049,6 +6967,32 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.1" tough-cookie "^2.3.3" +"request@>= 2.52.0", request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" @@ -6165,6 +7109,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -6184,6 +7133,13 @@ rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -6306,7 +7262,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -6326,7 +7282,14 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.4: +saslprep@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" + integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag== + dependencies: + sparse-bitfield "^3.0.3" + +sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -6371,11 +7334,23 @@ semver-diff@^2.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= + semver@^6.0.0, semver@^6.1.2, semver@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.1.1, semver@^7.3.2, semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -6395,6 +7370,11 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" @@ -6552,6 +7532,11 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6637,6 +7622,23 @@ socket.io@^2.3.0: socket.io-client "2.3.0" socket.io-parser "~3.4.0" +socks-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" + integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== + dependencies: + agent-base "6" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.0.tgz#6b984928461d39871b3666754b9000ecf39dfac2" + integrity sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -6694,6 +7696,13 @@ sourcemap-codec@^1.4.4: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +sparse-bitfield@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + integrity sha1-/0rm5oZWBWuks+eSqzM004JzyhE= + dependencies: + memory-pager "^1.0.2" + spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" @@ -6732,6 +7741,18 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^3.1.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +sprintf-js@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -6744,6 +7765,18 @@ sql-formatter@^2.3.3: dependencies: lodash "^4.16.0" +sqlstring@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" + integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== + +ssf@~0.11.2: + version "0.11.2" + resolved "https://registry.yarnpkg.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c" + integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g== + dependencies: + frac "~1.1.2" + ssh2-streams@~0.4.10: version "0.4.10" resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" @@ -6782,6 +7815,13 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + stack-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" @@ -6837,6 +7877,13 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +stream-transform@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-2.1.0.tgz#e68cc062cced5b8ee669ae97f4be473eee5d9227" + integrity sha512-bwQO+75rzQbug7e5OOHnOR3FgbJ0fCjHmDIdynkwUaFzleBXugGmv2dx3sX3aIHUQRLjrcisRPgN9BWl63uGgw== + dependencies: + mixme "^0.5.0" + streamsearch@0.1.2, streamsearch@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -7085,6 +8132,18 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +tar@^6.0.2, tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tar@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" @@ -7097,6 +8156,23 @@ tar@^6.0.5: mkdirp "^1.0.3" yallist "^4.0.0" +tedious@^9.2.3: + version "9.2.3" + resolved "https://registry.yarnpkg.com/tedious/-/tedious-9.2.3.tgz#ac871f764dbde7b23e35c47379d2796611e52ef1" + integrity sha512-+mI2r/5mqxpTHKBZ/SW+NNH2MK5i3Pwwkw0gF5ZrS2wf2uT/03bLSss8nm7xh604abJXyjx0sirhwH63H328qA== + dependencies: + "@azure/ms-rest-nodeauth" "^3.0.6" + "@js-joda/core" "^3.1.0" + adal-node "^0.1.28" + bl "^3.0.0" + depd "^2.0.0" + iconv-lite "^0.6.2" + jsbi "^3.1.3" + native-duplexpair "^1.0.0" + punycode "^2.1.0" + readable-stream "^3.6.0" + sprintf-js "^1.1.2" + term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" @@ -7263,7 +8339,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^2.3.3, tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -7271,6 +8347,15 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -7307,6 +8392,11 @@ ts-jest@^25.2.1: semver "^5.5" yargs-parser "^16.1.0" +tslib@^1.10.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -7329,6 +8419,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -7386,6 +8481,11 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" +"underscore@>= 1.3.1": + version "1.13.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.0.tgz#3ccdcbb824230fc6bf234ad0ddcd83dff4eafe5f" + integrity sha512-sCs4H3pCytsb5K7i072FAEC9YlSYFIbosvM0tAKAlpSSUgD7yC1iXSEGdl5XrDKQ1YUB+p/HDzYrSG2H2Vl36g== + underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" @@ -7532,7 +8632,7 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^3.3.2, uuid@^3.4.0: +uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -7555,6 +8655,13 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -7703,7 +8810,7 @@ which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -7724,11 +8831,21 @@ widest-line@^2.0.0: dependencies: string-width "^2.1.1" +wmf@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da" + integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw== + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +word@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961" + integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== + worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -7806,16 +8923,55 @@ xdg-basedir@^3.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= +xlsx@^0.16.8: + version "0.16.9" + resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.16.9.tgz#dacd5bb46bda6dd3743940c9c3dc1e2171826256" + integrity sha512-gxi1I3EasYvgCX1vN9pGyq920Ron4NO8PNfhuoA3Hpq6Y8f0ECXiy4OLrK4QZBnj1jx3QD+8Fq5YZ/3mPZ5iXw== + dependencies: + adler-32 "~1.2.0" + cfb "^1.1.4" + codepage "~1.14.0" + commander "~2.17.1" + crc-32 "~1.2.0" + exit-on-epipe "~1.0.1" + fflate "^0.3.8" + ssf "~0.11.2" + wmf "~1.0.1" + word "~0.3.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@^0.4.19: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + +"xmldom@>= 0.1.x": + version "0.5.0" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" + integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== + xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +xpath.js@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1" + integrity sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ== + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"