├── .gitignore ├── LICENSE ├── README.md ├── lua ├── dbout │ ├── client.lua │ ├── cmd.lua │ ├── config.lua │ ├── connection.lua │ ├── init.lua │ ├── keymap.lua │ ├── rpc.lua │ ├── saver.lua │ ├── snacks.lua │ ├── ui │ │ ├── inspector.lua │ │ ├── queryer.lua │ │ ├── viewer.lua │ │ └── winbar.lua │ └── utils.lua └── telescope │ └── _extensions │ └── dbout.lua ├── package-lock.json ├── package.json ├── server ├── consumer.js ├── db │ ├── mssql.js │ ├── mysql.js │ ├── postgres.js │ └── sqlite.js ├── driver.js ├── main.js └── rpc.js └── stylua.toml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 zongben 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dbout.nvim 2 | 3 | **dbout.nvim** is a Neovim plugin that helps you connect to databases, execute SQL queries, and display the results in **JSON format**. 4 | No need to switch to external tools — everything happens inside Neovim, making your workflow faster and smoother. 5 | 6 | 圖片 7 | 8 | https://github.com/user-attachments/assets/21d4295a-897b-422a-aa69-2d6cde4e555d 9 | 10 | ## Key Features 11 | 12 | - **JSON Result Display**: View query results in a structured JSON format for easy reading and further processing. 13 | - **No More Connection Strings In Your Neovim Config**: All your database connections are securely saved locally on your machine. 14 | - **LSP Support**: Use `sqls` as the SQL language server, and spins up separate LSP instances per database connection to avoid mixing completions across different databases. 15 | 16 | ## Supported Databases 17 | 18 | - SQLite 19 | - PostgreSQL 20 | - MySQL 21 | - MSSQL 22 | 23 | ## Installation 24 | 25 | Requirements: 26 | 27 | - [Nodejs](https://github.com/nodejs/node) 28 | - [sqls](https://github.com/sqls-server/sqls) 29 | 30 | `sqls` is recommended to be installed via [mason.nvim](https://github.com/mason-org/mason.nvim). If you are not using mason, make sure sqls is installed and available in your system PATH. 31 | 32 | With lazy.nvim: 33 | 34 | ```lua 35 | { 36 | "zongben/dbout.nvim", 37 | build = "npm install", 38 | lazy = "VeryLazy", 39 | cmd = { "Dbout" }, 40 | config = function() 41 | require("dbout").setup({}) 42 | end, 43 | } 44 | ``` 45 | 46 | ## Configuration 47 | 48 | The default configuration is as follows: 49 | 50 | ```lua 51 | { 52 | keymaps = { 53 | queryer = { 54 | query = "", 55 | format = "", 56 | open_inspector = "", 57 | }, 58 | viewer = { 59 | close = "q", 60 | }, 61 | inspector = { 62 | close = "q", 63 | next_tab = "L", 64 | previous_tab = "H", 65 | inspect = "I", 66 | back = "", 67 | }, 68 | }, 69 | } 70 | ``` 71 | 72 | ## Usage 73 | 74 | Use the following commands for database connection management: 75 | 76 | `:Dbout OpenConnection` - Open a new buffer and connect to selected database connection 77 | `:Dbout NewConnection` - Create a new database connection 78 | `:Dbout DeleteConnection` - Delete an existing connection 79 | `:Dbout EditConnection` - Edit an existing connection 80 | `:Dbout AttachConnection` - Attach to selected connection in the current buffer (this is very useful after opening a .sql file) 81 | 82 | After opening or attaching a connection, a buffer for that database connection is created, named Queryer. 83 | Inside the Queryer buffer: 84 | 85 | `F5` - Execute the current SQL query 86 | `F11` - Format SQL 87 | `F12` - Open Inspector 88 | 89 | The Inspector is a buffer used for inspecting database objects. 90 | Within the Inspector buffer: 91 | 92 | `H` and `L` - Switch between tabs 93 | `I` - Inspect more details, such as table columns, triggers, views, etc. 94 | 95 | ### Telescope Extension 96 | 97 | For users with Telescope installed, you can load the dbout extension for easier database connection management: 98 | 99 | ```lua 100 | require("telescope").load_extension("dbout") 101 | 102 | --default config 103 | require("telescope").setup({ 104 | extensions = { 105 | dbout = { 106 | keymaps = { 107 | open_connection = "", 108 | new_connection = "n", 109 | delete_connection = "d", 110 | edit_connection = "e", 111 | attach_connection = "a", 112 | }, 113 | }, 114 | }, 115 | }) 116 | ``` 117 | 118 | Then, you can open the database connection picker by calling `:Telescope dbout` or `require("telescope").extensions.dbout.dbout()` 119 | 120 | ### Snacks Sources 121 | 122 | For users with Snacks installed, dbout automatically registers its sources. 123 | You can open the database connection manager simply by calling `:Dbout` 124 | 125 | ```lua 126 | --default config 127 | require("dbout.snacks").setup({ 128 | keymaps = { 129 | open_connection = "", 130 | new_connection = "n", 131 | delete_connection = "d", 132 | edit_connection = "e", 133 | attach_connection = "a", 134 | }, 135 | }) 136 | 137 | -- You can also configure the source: 138 | Snacks.picker.sources.dbout = { 139 | -- your options here 140 | -- For more details, see: 141 | -- https://github.com/folke/snacks.nvim/blob/main/docs/picker.md 142 | } 143 | ``` 144 | 145 | ## NOTES 146 | 147 | When opening a connection, you might see an **Sqls connection error**. 148 | However, based on my tests, Sqls is actually connecting to the server successfully. 149 | This error message seems to be an issue with Sqls itself. For now, I’m not sure how to disable it, so I suggest simply ignoring this error message. 150 | 151 | -- 152 | 153 | If you’re using [mason-lspconfig](https://github.com/mason-org/mason-lspconfig.nvim) to automatically start LSP servers, I recommend excluding sqls from it. 154 | dbout will automatically start sqls for you. 155 | 156 | ```lua 157 | require("mason-lspconfig").setup({ 158 | automatic_enable = { 159 | exclude = { 160 | "sqls", 161 | }, 162 | }, 163 | }) 164 | ``` 165 | 166 | ## TODO 167 | 168 | - [ ] CSV output 169 | - [ ] query history 170 | - [ ] layout system 171 | - [ ] better LSP and tree sitting support 172 | - [ ] mongodb support 173 | -------------------------------------------------------------------------------- /lua/dbout/client.lua: -------------------------------------------------------------------------------- 1 | local rpc = require("dbout.rpc") 2 | 3 | local send_rpc = function(method, param, cb) 4 | rpc.send_jsonrpc(method, param, function(jsonstr) 5 | cb(jsonstr) 6 | end) 7 | end 8 | 9 | local M = {} 10 | 11 | M.get_table_list = function(id, cb) 12 | send_rpc("get_table_list", { id = id }, cb) 13 | end 14 | 15 | M.get_view_list = function(id, cb) 16 | send_rpc("get_view_list", { id = id }, cb) 17 | end 18 | 19 | M.get_view = function(id, view_name, cb) 20 | send_rpc("get_view", { id = id, view_name = view_name }, cb) 21 | end 22 | 23 | M.get_store_procedure = function(id, procedure_name, cb) 24 | send_rpc("get_store_procedure", { id = id, procedure_name = procedure_name }, cb) 25 | end 26 | 27 | M.get_store_procedure_list = function(id, cb) 28 | send_rpc("get_store_procedure_list", { id = id }, cb) 29 | end 30 | 31 | M.get_function_list = function(id, cb) 32 | send_rpc("get_function_list", { id = id }, cb) 33 | end 34 | 35 | M.get_trigger_list = function(id, table_name, cb) 36 | send_rpc("get_trigger_list", { id = id, table_name = table_name }, cb) 37 | end 38 | 39 | M.get_function = function(id, function_name, cb) 40 | send_rpc("get_function", { id = id, function_name = function_name }, cb) 41 | end 42 | 43 | M.get_table = function(id, table_name, cb) 44 | send_rpc("get_table", { id = id, table_name = table_name }, cb) 45 | end 46 | 47 | M.get_trigger = function(id, trig_name, cb) 48 | send_rpc("get_trigger", { id = id, trig_name = trig_name }, cb) 49 | end 50 | 51 | M.generate_select_sql = function(id, table_name, cb) 52 | send_rpc("generate_select_sql", { id = id, table_name = table_name }, cb) 53 | end 54 | 55 | M.generate_update_sql = function(id, table_name, cb) 56 | send_rpc("generate_update_sql", { id = id, table_name = table_name }, cb) 57 | end 58 | 59 | M.generate_insert_sql = function(id, table_name, cb) 60 | send_rpc("generate_insert_sql", { id = id, table_name = table_name }, cb) 61 | end 62 | 63 | M.query = function(id, sql, cb) 64 | send_rpc("query", { id = id, sql = sql }, cb) 65 | end 66 | 67 | M.format = function(id, sql, cb) 68 | send_rpc("format", { id = id, sql = sql }, cb) 69 | end 70 | 71 | return M 72 | -------------------------------------------------------------------------------- /lua/dbout/cmd.lua: -------------------------------------------------------------------------------- 1 | local rpc = require("dbout.rpc") 2 | local conn = require("dbout.connection") 3 | local queryer = require("dbout.ui.queryer") 4 | 5 | local args = { 6 | new_connection = "NewConnection", 7 | edit_connection = "EditConnection", 8 | delete_connection = "DeleteConnection", 9 | open_connection = "OpenConnection", 10 | attach_connection = "AttachConnection", 11 | } 12 | 13 | local select_connection = function(cb) 14 | vim.ui.select(conn.get_connections(), { 15 | prompt = "Choose a connection", 16 | format_item = function(item) 17 | return item.name .. " " .. item.db_type .. ":" .. item.connstr 18 | end, 19 | }, function(connection) 20 | if not connection then 21 | return 22 | end 23 | cb(connection) 24 | end) 25 | end 26 | 27 | local new_connection = function() 28 | conn.create_connection({}, function(c) 29 | conn.add_connection(c) 30 | end) 31 | end 32 | 33 | local edit_connection = function() 34 | select_connection(function(c) 35 | conn.create_connection(c, function(cn) 36 | conn.update_connection(cn) 37 | end) 38 | end) 39 | end 40 | 41 | local delete_connection = function() 42 | select_connection(function(c) 43 | conn.remove_connection(c.id) 44 | end) 45 | end 46 | 47 | local open_connection = function() 48 | select_connection(function(c) 49 | conn.open_connection(c, function() 50 | queryer.create_buf(c) 51 | end) 52 | end) 53 | end 54 | 55 | local attach_connection = function() 56 | select_connection(function(c) 57 | conn.open_connection(c, function() 58 | queryer.attach_buf(c, vim.api.nvim_get_current_buf()) 59 | end) 60 | end) 61 | end 62 | 63 | local M = {} 64 | 65 | M.init = function() 66 | vim.api.nvim_create_user_command("Dbout", function(opts) 67 | if not rpc.is_alive() then 68 | rpc.server_up() 69 | end 70 | 71 | local cmd = opts.args 72 | 73 | if Snacks and cmd == "" then 74 | require("dbout.snacks").open_picker() 75 | end 76 | 77 | if cmd == args.new_connection then 78 | new_connection() 79 | elseif cmd == args.edit_connection then 80 | edit_connection() 81 | elseif cmd == args.delete_connection then 82 | delete_connection() 83 | elseif cmd == args.open_connection then 84 | open_connection() 85 | elseif cmd == args.attach_connection then 86 | attach_connection() 87 | end 88 | end, { 89 | nargs = "?", 90 | complete = function(_, line, pos) 91 | local arg = line:sub(pos + 1) 92 | local matches = {} 93 | for _, opt in pairs(args) do 94 | if opt:match("^" .. arg) then 95 | table.insert(matches, opt) 96 | end 97 | end 98 | return matches 99 | end, 100 | }) 101 | end 102 | 103 | return M 104 | -------------------------------------------------------------------------------- /lua/dbout/config.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.defaults = { 4 | keymaps = { 5 | queryer = { 6 | query = "", 7 | open_inspector = "", 8 | format = "", 9 | }, 10 | viewer = { 11 | close = "q", 12 | }, 13 | inspector = { 14 | close = "q", 15 | next_tab = "L", 16 | previous_tab = "H", 17 | inspect = "I", 18 | back = "", 19 | }, 20 | }, 21 | } 22 | 23 | return M 24 | -------------------------------------------------------------------------------- /lua/dbout/connection.lua: -------------------------------------------------------------------------------- 1 | local saver = require("dbout.saver") 2 | local utils = require("dbout.utils") 3 | local rpc = require("dbout.rpc") 4 | 5 | local connections = {} 6 | local supported_db = { "sqlite3", "postgresql", "mysql", "mssql" } 7 | 8 | local M = {} 9 | 10 | local save = function() 11 | saver.save(connections) 12 | end 13 | 14 | M.init = function() 15 | connections = saver.load() or {} 16 | end 17 | 18 | M.create_connection = function(connection, cb) 19 | local fn = function(db_type) 20 | local name = vim.fn.input("Enter name: ", connection.name or "") 21 | if name == "" then 22 | return 23 | end 24 | 25 | if M.is_conn_exists(connection.id or "", name) then 26 | vim.notify(name .. " is used.", vim.log.levels.ERROR) 27 | return 28 | end 29 | 30 | local connstr = vim.fn.input("Enter " .. db_type .. " connection string: ", connection.connstr or "") 31 | if connstr == "" then 32 | return 33 | end 34 | 35 | cb({ 36 | id = connection.id or utils.generate_uuid(), 37 | name = name, 38 | db_type = db_type, 39 | connstr = connstr, 40 | }) 41 | end 42 | 43 | if connection.id then 44 | fn(connection.db_type) 45 | return 46 | end 47 | 48 | vim.ui.select(M.get_supported_db(), { 49 | prompt = "Choose a database", 50 | }, function(db_type) 51 | if not db_type then 52 | return 53 | end 54 | fn(db_type) 55 | end) 56 | end 57 | 58 | M.is_conn_exists = function(id, name) 59 | return #vim.tbl_filter(function(c) 60 | return c.id ~= id and c.name == name 61 | end, connections) > 0 62 | end 63 | 64 | M.get_connections = function() 65 | return connections 66 | end 67 | 68 | M.get_supported_db = function() 69 | return supported_db 70 | end 71 | 72 | M.add_connection = function(conn) 73 | table.insert(connections, conn) 74 | save() 75 | end 76 | 77 | M.remove_connection = function(id) 78 | connections = vim.tbl_filter(function(c) 79 | return c.id ~= id 80 | end, connections) 81 | save() 82 | end 83 | 84 | M.update_connection = function(conn) 85 | for _, c in ipairs(connections) do 86 | if c.id == conn.id then 87 | c.name = conn.name 88 | c.db_type = conn.db_type 89 | c.connstr = conn.connstr 90 | save() 91 | return 92 | end 93 | end 94 | end 95 | 96 | M.open_connection = function(conn, cb) 97 | rpc.send_jsonrpc("create_connection", { 98 | id = conn.id, 99 | dbType = conn.db_type, 100 | connStr = conn.connstr, 101 | }, function() 102 | cb() 103 | end) 104 | end 105 | 106 | return M 107 | -------------------------------------------------------------------------------- /lua/dbout/init.lua: -------------------------------------------------------------------------------- 1 | local config = require("dbout.config") 2 | local keymap = require("dbout.keymap") 3 | local cmd = require("dbout.cmd") 4 | local conn = require("dbout.connection") 5 | local queryer = require("dbout.ui.queryer") 6 | 7 | local M = {} 8 | 9 | M.setup = function(opts) 10 | M.options = vim.tbl_deep_extend("force", config.defaults, opts or {}) 11 | conn.init() 12 | keymap.init(M.options.keymaps) 13 | queryer.init() 14 | cmd.init() 15 | end 16 | 17 | return M 18 | -------------------------------------------------------------------------------- /lua/dbout/keymap.lua: -------------------------------------------------------------------------------- 1 | local viewer = require("dbout.ui.viewer") 2 | local queryer = require("dbout.ui.queryer") 3 | local inspector = require("dbout.ui.inspector") 4 | 5 | local M = {} 6 | 7 | local map = function(bufnr, mode, key, cb) 8 | if key == "" then 9 | return 10 | end 11 | vim.keymap.set(mode, key, cb, { buffer = bufnr, noremap = true, silent = true }) 12 | end 13 | 14 | M.init = function(keymaps) 15 | queryer.buffer_keymappings = function(buf) 16 | local q = keymaps.queryer 17 | map(buf, { "i", "v", "n" }, q.query, queryer.query) 18 | map(buf, { "i", "v", "n" }, q.format, queryer.format) 19 | map(buf, { "i", "n" }, q.open_inspector, queryer.open_inspector) 20 | end 21 | 22 | viewer.buffer_keymappings = function(buf) 23 | local v = keymaps.viewer 24 | map(buf, { "n" }, v.close, viewer.close_viewer) 25 | end 26 | 27 | inspector.buffer_keymappings = function(buf) 28 | local i = keymaps.inspector 29 | map(buf, { "n" }, i.close, inspector.close_inspector) 30 | map(buf, { "n" }, i.next_tab, inspector.next_tab) 31 | map(buf, { "n" }, i.previous_tab, inspector.previous_tab) 32 | map(buf, { "n" }, i.inspect, inspector.inspect) 33 | map(buf, { "n" }, i.back, inspector.back) 34 | end 35 | end 36 | 37 | return M 38 | -------------------------------------------------------------------------------- /lua/dbout/rpc.lua: -------------------------------------------------------------------------------- 1 | local utils = require("dbout.utils") 2 | 3 | local job_id 4 | local callbacks = {} 5 | local buffer = "" 6 | 7 | local M = {} 8 | 9 | M.server_up = function() 10 | local files = vim.api.nvim_get_runtime_file("server/main.js", false) 11 | job_id = vim.fn.jobstart({ 12 | "node", 13 | files[1], 14 | }, { 15 | on_stdout = function(_, raw) 16 | for _, chunk in ipairs(raw) do 17 | buffer = buffer .. chunk 18 | end 19 | 20 | local ok, data = pcall(vim.fn.json_decode, buffer) 21 | if not ok then 22 | return 23 | else 24 | buffer = "" 25 | end 26 | 27 | if callbacks[data.id] then 28 | callbacks[data.id](data.result) 29 | callbacks[data.id] = nil 30 | end 31 | end, 32 | on_stderr = function(_, raw) 33 | local data = vim.fn.json_decode(raw) 34 | if data.id and callbacks[data.id] then 35 | callbacks[data.id] = nil 36 | end 37 | vim.notify(data.error.message .. "\n" .. data.error.data, vim.log.levels.ERROR) 38 | end, 39 | }) 40 | end 41 | 42 | M.is_alive = function() 43 | return vim.fn.jobwait({ job_id }, 0)[1] == -1 44 | end 45 | 46 | M.send_jsonrpc = function(method, params, cb) 47 | local id = utils.generate_uuid() 48 | local jsonrpc = { 49 | jsonrpc = "2.0", 50 | id = id, 51 | method = method, 52 | params = params, 53 | } 54 | callbacks[id] = cb 55 | local json = vim.fn.json_encode(jsonrpc) 56 | -- vim.notify(json) 57 | vim.fn.chansend(job_id, json .. "\n") 58 | end 59 | 60 | M.send_notification = function(method, params) 61 | local jsonrpc = { 62 | jsonrpc = "2.0", 63 | method = method, 64 | params = params, 65 | } 66 | vim.fn.chansend(job_id, vim.fn.json_encode(jsonrpc) .. "\n") 67 | end 68 | 69 | return M 70 | -------------------------------------------------------------------------------- /lua/dbout/saver.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local statepath = vim.fn.stdpath("state") 4 | if type(statepath) == "table" then 5 | statepath = statepath[1] 6 | end 7 | local state_dir = vim.fs.joinpath(statepath, "dbout") 8 | local persist_file = vim.fs.joinpath(state_dir, "db_explorer.json") 9 | 10 | M.save = function(connection) 11 | local json = vim.fn.json_encode(connection) 12 | vim.fn.mkdir(state_dir, "p") 13 | vim.fn.writefile({ json }, persist_file) 14 | end 15 | 16 | M.load = function() 17 | local f = io.open(persist_file, "r") 18 | if not f then 19 | return 20 | end 21 | local content = f:read("*a") 22 | f:close() 23 | return vim.fn.json_decode(content) 24 | end 25 | 26 | return M 27 | -------------------------------------------------------------------------------- /lua/dbout/snacks.lua: -------------------------------------------------------------------------------- 1 | local conn = require("dbout.connection") 2 | local queryer = require("dbout.ui.queryer") 3 | 4 | local function truncate(text, width) 5 | if #text > width then 6 | return text:sub(1, width - 1) .. "…" 7 | end 8 | return text 9 | end 10 | 11 | local options = { 12 | keymaps = { 13 | open_connection = "", 14 | new_connection = "n", 15 | delete_connection = "d", 16 | edit_connection = "e", 17 | attach_connection = "a", 18 | }, 19 | } 20 | 21 | local M = {} 22 | 23 | M.setup = function(opts) 24 | options = vim.tbl_deep_extend("force", options, opts or {}) 25 | end 26 | 27 | M.open_picker = function() 28 | ---@class snacks.picker.Config 29 | local config = { 30 | source = "dbout", 31 | title = "Connections", 32 | preview = "none", 33 | layout = { 34 | preset = "select", 35 | }, 36 | finder = function() 37 | local items = {} 38 | for index, value in ipairs(conn.get_connections()) do 39 | table.insert(items, { 40 | idx = index, 41 | name = value.name, 42 | text = value.name, 43 | id = value.id, 44 | connstr = value.connstr, 45 | db_type = value.db_type, 46 | }) 47 | end 48 | return items 49 | end, 50 | format = function(item) 51 | return { 52 | { string.format("%-15s", truncate(item.name, 15)), "SnacksPickerLabel" }, 53 | { item.db_type .. ":" .. item.connstr, "SnacksPickerComment" }, 54 | } 55 | end, 56 | win = { 57 | input = { 58 | keys = { 59 | [options.keymaps.open_connection] = { "open_connection", mode = { "n" } }, 60 | [options.keymaps.new_connection] = { "new_connection", mode = { "n" } }, 61 | [options.keymaps.delete_connection] = { "delete_connection", mode = { "n" } }, 62 | [options.keymaps.edit_connection] = { "edit_connection", mode = { "n" } }, 63 | [options.keymaps.attach_connection] = { "attach_connection", mode = { "n" } }, 64 | }, 65 | }, 66 | }, 67 | actions = { 68 | confirm = function() end, 69 | new_connection = function(picker) 70 | picker:close() 71 | conn.create_connection({}, function(c) 72 | conn.add_connection(c) 73 | M.open_picker() 74 | end) 75 | end, 76 | delete_connection = function(picker, item) 77 | conn.remove_connection(item.id) 78 | picker:find() 79 | end, 80 | edit_connection = function(picker, item) 81 | conn.create_connection(item, function(c) 82 | conn.update_connection(c) 83 | picker:find() 84 | end) 85 | end, 86 | open_connection = function(picker, item) 87 | conn.open_connection(item, function() 88 | picker:close() 89 | queryer.create_buf(item) 90 | end) 91 | end, 92 | attach_connection = function(picker, item) 93 | conn.open_connection(item, function() 94 | picker:close() 95 | queryer.attach_buf(item, vim.api.nvim_get_current_buf()) 96 | end) 97 | end, 98 | }, 99 | } 100 | 101 | Snacks.picker.pick(config) 102 | end 103 | 104 | return M 105 | -------------------------------------------------------------------------------- /lua/dbout/ui/inspector.lua: -------------------------------------------------------------------------------- 1 | local utils = require("dbout.utils") 2 | local client = require("dbout.client") 3 | local winbar = require("dbout.ui.winbar") 4 | 5 | local inspector_bufnr 6 | local conn 7 | local queryer_bufnr 8 | 9 | local set_inspector_buf = function() 10 | local tab = winbar.get_current_tab() 11 | 12 | local fn = function(jsonstr) 13 | local lines = utils.split_json(jsonstr) 14 | utils.set_buf_lines(inspector_bufnr, lines) 15 | end 16 | 17 | if tab == "Tables" then 18 | client.get_table_list(conn.id, fn) 19 | elseif tab == "Views" then 20 | client.get_view_list(conn.id, fn) 21 | elseif tab == "StoreProcedures" then 22 | client.get_store_procedure_list(conn.id, fn) 23 | elseif tab == "Functions" then 24 | client.get_function_list(conn.id, fn) 25 | elseif tab == "Columns" then 26 | client.get_table(conn.id, winbar.get_sub_tab_table(), fn) 27 | elseif tab == "Triggers" then 28 | client.get_trigger_list(conn.id, winbar.get_sub_tab_table(), fn) 29 | end 30 | end 31 | 32 | local inspect_view = function() 33 | client.get_view_list(conn.id, function(jsonstr) 34 | local data = vim.fn.json_decode(jsonstr) 35 | vim.ui.select(data.rows, { 36 | prompt = "Inspect a view", 37 | format_item = function(item) 38 | return item.view_name 39 | end, 40 | }, function(view) 41 | if not view then 42 | return 43 | end 44 | client.get_view(conn.id, view.view_name, function(v_jsonstr) 45 | local v = vim.fn.json_decode(v_jsonstr).rows[1].definition 46 | local lines = vim.split(v, "\r?\n") 47 | utils.set_buf_lines(queryer_bufnr, lines) 48 | end) 49 | end) 50 | end) 51 | end 52 | 53 | local inspect_store_procedure = function() 54 | client.get_store_procedure_list(conn.id, function(jsonstr) 55 | local data = vim.fn.json_decode(jsonstr) 56 | vim.ui.select(data.rows, { 57 | prompt = "Inspect a store procedure", 58 | format_item = function(item) 59 | return item.procedure_name 60 | end, 61 | }, function(procedure) 62 | if not procedure then 63 | return 64 | end 65 | client.get_store_procedure(conn.id, procedure.procedure_name, function(sp_jsonstr) 66 | local sp = vim.fn.json_decode(sp_jsonstr).rows[1].definition 67 | local lines = vim.split(sp, "\r?\n") 68 | utils.set_buf_lines(queryer_bufnr, lines) 69 | end) 70 | end) 71 | end) 72 | end 73 | 74 | local inspect_function = function() 75 | client.get_function_list(conn.id, function(jsonstr) 76 | local data = vim.fn.json_decode(jsonstr) 77 | vim.ui.select(data.rows, { 78 | prompt = "Inspect a function", 79 | format_item = function(item) 80 | return item.function_name 81 | end, 82 | }, function(f) 83 | if not f then 84 | return 85 | end 86 | client.get_function(conn.id, f.function_name, function(f_jsonstr) 87 | local sp = vim.fn.json_decode(f_jsonstr).rows[1].definition 88 | local lines = vim.split(sp, "\r?\n") 89 | utils.set_buf_lines(queryer_bufnr, lines) 90 | end) 91 | end) 92 | end) 93 | end 94 | 95 | local inspect_table = function() 96 | client.get_table_list(conn.id, function(jsonstr) 97 | local data = vim.fn.json_decode(jsonstr) 98 | vim.ui.select(data.rows, { 99 | prompt = "Inspect a table", 100 | format_item = function(item) 101 | return item.table_name 102 | end, 103 | }, function(t) 104 | if not t then 105 | return 106 | end 107 | local winnr = utils.get_or_create_buf_win(inspector_bufnr) 108 | vim.api.nvim_win_set_buf(winnr, inspector_bufnr) 109 | winbar.set_sub_tab_table(t.table_name) 110 | winbar.tab_switch(2) 111 | winbar.set_winbar(winnr) 112 | set_inspector_buf() 113 | end) 114 | end) 115 | end 116 | 117 | local inspect_trigger = function() 118 | client.get_trigger_list(conn.id, winbar.get_sub_tab_table(), function(jsonstr) 119 | local data = vim.fn.json_decode(jsonstr) 120 | vim.ui.select(data.rows, { 121 | prompt = "Inspect a trigger", 122 | format_item = function(item) 123 | return item.trigger_name 124 | end, 125 | }, function(t) 126 | if not t then 127 | return 128 | end 129 | client.get_trigger(conn.id, t.trigger_name, function(t_jsonstr) 130 | local sp = vim.fn.json_decode(t_jsonstr).rows[1].definition 131 | local lines = vim.split(sp, "\r?\n") 132 | utils.set_buf_lines(queryer_bufnr, lines) 133 | end) 134 | end) 135 | end) 136 | end 137 | 138 | local inspect_column = function() 139 | local methods = { 140 | "SELECT", 141 | "INSERT", 142 | "UPDATE", 143 | } 144 | vim.ui.select(methods, { 145 | prompt = "Inspect a method", 146 | }, function(m) 147 | if not m then 148 | return 149 | end 150 | local fn = function(s_jsonstr) 151 | local sql = vim.fn.json_decode(s_jsonstr) 152 | local lines = vim.split(sql, "\r?\n") 153 | utils.set_buf_lines(queryer_bufnr, lines) 154 | end 155 | if m == "SELECT" then 156 | client.generate_select_sql(conn.id, winbar.get_sub_tab_table(), fn) 157 | elseif m == "UPDATE" then 158 | client.generate_update_sql(conn.id, winbar.get_sub_tab_table(), fn) 159 | elseif m == "INSERT" then 160 | client.generate_insert_sql(conn.id, winbar.get_sub_tab_table(), fn) 161 | end 162 | end) 163 | end 164 | 165 | local M = {} 166 | 167 | M.buffer_keymappings = nil 168 | 169 | M.open_inspector = function(connection, bufnr) 170 | conn = connection 171 | queryer_bufnr = bufnr 172 | 173 | if inspector_bufnr == nil then 174 | inspector_bufnr = vim.api.nvim_create_buf(false, true) 175 | M.buffer_keymappings(inspector_bufnr) 176 | end 177 | vim.api.nvim_set_option_value("filetype", "json", { buf = inspector_bufnr }) 178 | 179 | local winnr = utils.get_or_create_buf_win(inspector_bufnr) 180 | vim.api.nvim_win_set_buf(winnr, inspector_bufnr) 181 | winbar.set_winbar(winnr) 182 | set_inspector_buf() 183 | end 184 | 185 | M.reset = function() 186 | utils.close_buf_win(inspector_bufnr) 187 | winbar.reset() 188 | end 189 | 190 | M.close_inspector = function() 191 | utils.close_buf_win(inspector_bufnr) 192 | end 193 | 194 | M.next_tab = function() 195 | winbar.next_tab() 196 | local winnr = utils.get_buf_win(inspector_bufnr) 197 | winbar.set_winbar(winnr) 198 | set_inspector_buf() 199 | end 200 | 201 | M.previous_tab = function() 202 | winbar.previous_tab() 203 | local winnr = utils.get_buf_win(inspector_bufnr) 204 | winbar.set_winbar(winnr) 205 | set_inspector_buf() 206 | end 207 | 208 | M.inspect = function() 209 | local tab = winbar.get_current_tab() 210 | 211 | if tab == "Tables" then 212 | inspect_table() 213 | elseif tab == "Views" then 214 | inspect_view() 215 | elseif tab == "StoreProcedures" then 216 | if conn.db_type == "sqlite3" then 217 | return 218 | end 219 | inspect_store_procedure() 220 | elseif tab == "Functions" then 221 | if conn.db_type == "sqlite3" then 222 | return 223 | end 224 | inspect_function() 225 | elseif tab == "Triggers" then 226 | inspect_trigger() 227 | elseif tab == "Columns" then 228 | inspect_column() 229 | end 230 | end 231 | 232 | M.back = function() 233 | winbar.back() 234 | local winnr = utils.get_buf_win(inspector_bufnr) 235 | winbar.set_winbar(winnr) 236 | set_inspector_buf() 237 | end 238 | 239 | return M 240 | -------------------------------------------------------------------------------- /lua/dbout/ui/queryer.lua: -------------------------------------------------------------------------------- 1 | local utils = require("dbout.utils") 2 | local client = require("dbout.client") 3 | local viewer = require("dbout.ui.viewer") 4 | local inspector = require("dbout.ui.inspector") 5 | 6 | local buffer_connection = {} 7 | 8 | local set_winbar = function(name) 9 | return "%#Title#Database:[" .. name .. "]%*" 10 | end 11 | 12 | local visual_select = function() 13 | local start_row, end_row 14 | if vim.fn.mode():match("[vV\22]") then 15 | local v_row = vim.fn.getpos("v")[2] 16 | local c_row = vim.fn.getpos(".")[2] 17 | 18 | if v_row < c_row then 19 | start_row = v_row 20 | end_row = c_row 21 | else 22 | start_row = c_row 23 | end_row = v_row 24 | end 25 | start_row = start_row - 1 26 | else 27 | start_row = 0 28 | end_row = -1 29 | end 30 | return start_row, end_row 31 | end 32 | 33 | local M = {} 34 | 35 | M.buffer_keymappings = nil 36 | 37 | local start_lsp = function(conn) 38 | local lsp_name = "sqls" .. "_" .. conn.name 39 | vim.lsp.config[lsp_name] = { 40 | cmd = { "sqls" }, 41 | filetypes = { "sql" }, 42 | root_dir = function(bufnr, on_dir) 43 | if buffer_connection[bufnr] and buffer_connection[bufnr].name == conn.name then 44 | on_dir() 45 | end 46 | end, 47 | settings = { 48 | sqls = { 49 | connections = { 50 | { 51 | driver = conn.db_type, 52 | dataSourceName = conn.connstr, 53 | }, 54 | }, 55 | }, 56 | }, 57 | } 58 | vim.lsp.enable(lsp_name, true) 59 | end 60 | 61 | local buf_detach_lsp = function(bufnr) 62 | local clients = vim.lsp.get_clients({ bufnr = bufnr }) 63 | for _, c in ipairs(clients) do 64 | if c.name:match("^sqls") then 65 | buffer_connection[bufnr] = nil 66 | vim.lsp.buf_detach_client(bufnr, c.id) 67 | end 68 | end 69 | end 70 | 71 | M.init = function() 72 | vim.api.nvim_create_autocmd("BufEnter", { 73 | callback = function(args) 74 | local conn = buffer_connection[args.buf] 75 | if not conn then 76 | return 77 | end 78 | vim.wo.winbar = set_winbar(conn.name) 79 | end, 80 | }) 81 | end 82 | 83 | local set_connection_buf = function(connection, bufnr) 84 | vim.api.nvim_set_option_value("filetype", "sql", { buf = bufnr }) 85 | buffer_connection[bufnr] = connection 86 | 87 | M.buffer_keymappings(bufnr) 88 | 89 | if vim.api.nvim_get_current_buf() == bufnr then 90 | vim.wo.winbar = set_winbar(connection.name) 91 | end 92 | end 93 | 94 | M.create_buf = function(connection) 95 | local bufnr = vim.api.nvim_create_buf(true, false) 96 | set_connection_buf(connection, bufnr) 97 | utils.switch_win_to_buf(bufnr) 98 | start_lsp(connection) 99 | end 100 | 101 | M.attach_buf = function(connection, bufnr) 102 | buf_detach_lsp(bufnr) 103 | set_connection_buf(connection, bufnr) 104 | utils.switch_win_to_buf(bufnr) 105 | start_lsp(connection) 106 | inspector.reset() 107 | end 108 | 109 | M.query = function() 110 | local win = vim.api.nvim_get_current_win() 111 | local bufnr = vim.api.nvim_win_get_buf(win) 112 | 113 | local start_row, end_row = visual_select() 114 | 115 | local sql = table.concat(vim.api.nvim_buf_get_lines(bufnr, start_row, end_row, false), "\n") 116 | client.query(buffer_connection[bufnr].id, sql, function(jsonstr) 117 | viewer.open_viewer(jsonstr) 118 | end) 119 | end 120 | 121 | M.open_inspector = function() 122 | local win = vim.api.nvim_get_current_win() 123 | local bufnr = vim.api.nvim_win_get_buf(win) 124 | inspector.open_inspector(buffer_connection[bufnr], bufnr) 125 | end 126 | 127 | M.format = function() 128 | local win = vim.api.nvim_get_current_win() 129 | local bufnr = vim.api.nvim_win_get_buf(win) 130 | 131 | local start_row, end_row = visual_select() 132 | 133 | local sql = table.concat(vim.api.nvim_buf_get_lines(bufnr, start_row, end_row, false), "\n") 134 | client.format(buffer_connection[bufnr].id, sql, function(jsonstr) 135 | local str = vim.fn.json_decode(jsonstr) 136 | local lines = vim.split(str, "\r?\n") 137 | utils.set_buf_lines(bufnr, lines) 138 | vim.api.nvim_win_set_buf(win, bufnr) 139 | end) 140 | end 141 | 142 | return M 143 | -------------------------------------------------------------------------------- /lua/dbout/ui/viewer.lua: -------------------------------------------------------------------------------- 1 | local utils = require("dbout.utils") 2 | 3 | local viewer_bufnr 4 | 5 | local M = {} 6 | 7 | M.buffer_keymappings = nil 8 | 9 | M.open_viewer = function(jsonstr) 10 | if viewer_bufnr == nil then 11 | viewer_bufnr = vim.api.nvim_create_buf(false, true) 12 | M.buffer_keymappings(viewer_bufnr) 13 | end 14 | vim.api.nvim_set_option_value("filetype", "json", { buf = viewer_bufnr }) 15 | 16 | 17 | local lines = utils.split_json(jsonstr) 18 | utils.set_buf_lines(viewer_bufnr, lines) 19 | 20 | local winnr = utils.get_or_create_buf_win(viewer_bufnr) 21 | vim.api.nvim_win_set_buf(winnr, viewer_bufnr) 22 | vim.api.nvim_set_option_value("winbar", "%#Title#[Query Result]%*", { win = winnr }) 23 | end 24 | 25 | M.close_viewer = function() 26 | utils.close_buf_win(viewer_bufnr) 27 | end 28 | 29 | return M 30 | -------------------------------------------------------------------------------- /lua/dbout/ui/winbar.lua: -------------------------------------------------------------------------------- 1 | local tab_switch = 1 2 | local tab_state = { 3 | { index = 1, tabs = { 4 | "Tables", 5 | "Views", 6 | "StoreProcedures", 7 | "Functions", 8 | } }, 9 | { index = 1, tabs = { 10 | "Columns", 11 | "Triggers", 12 | } }, 13 | } 14 | local sub_tab_table_name 15 | 16 | local set_top_winbar = function(winnr) 17 | local state = tab_state[1] 18 | local tab_index = state.index 19 | local tabs = state.tabs 20 | 21 | local bar = {} 22 | for index, tab in ipairs(tabs) do 23 | if index == tab_index then 24 | table.insert(bar, "%#Title#[" .. tab .. "]%*") 25 | else 26 | table.insert(bar, tab) 27 | end 28 | end 29 | vim.api.nvim_set_option_value("winbar", table.concat(bar, "|"), { win = winnr }) 30 | vim.api.nvim_win_set_cursor(winnr, { 1, 0 }) 31 | end 32 | 33 | local set_sub_winbar = function(winnr) 34 | local state = tab_state[2] 35 | 36 | local bar = {} 37 | table.insert(bar, "<--Back") 38 | table.insert(bar, " %#Directory#" .. sub_tab_table_name .. "%* ") 39 | 40 | for index, tab in ipairs(state.tabs) do 41 | if index == state.index then 42 | table.insert(bar, "%#Title#[" .. tab .. "]%*") 43 | else 44 | table.insert(bar, tab) 45 | end 46 | end 47 | 48 | vim.api.nvim_set_option_value("winbar", table.concat(bar, "|"), { win = winnr }) 49 | vim.api.nvim_win_set_cursor(winnr, { 1, 0 }) 50 | end 51 | 52 | local M = {} 53 | 54 | M.set_sub_tab_table = function(table_name) 55 | sub_tab_table_name = table_name 56 | end 57 | 58 | M.get_sub_tab_table = function() 59 | return sub_tab_table_name 60 | end 61 | 62 | M.set_winbar = function(winnr) 63 | if tab_switch == 1 then 64 | set_top_winbar(winnr) 65 | elseif tab_switch == 2 then 66 | set_sub_winbar(winnr) 67 | end 68 | end 69 | 70 | M.tab_switch = function(tabnr) 71 | tab_switch = tabnr 72 | end 73 | 74 | M.next_tab = function() 75 | local state = tab_state[tab_switch] 76 | state.index = state.index + 1 77 | if state.index > #state.tabs then 78 | state.index = 1 79 | end 80 | end 81 | 82 | M.previous_tab = function() 83 | local state = tab_state[tab_switch] 84 | state.index = state.index - 1 85 | if state.index < 1 then 86 | state.index = #state.tabs 87 | end 88 | end 89 | 90 | M.get_current_tab = function() 91 | local state = tab_state[tab_switch] 92 | local tab = state.tabs[state.index] 93 | return tab 94 | end 95 | 96 | M.reset = function() 97 | M.tab_switch(1) 98 | tab_state[1].index = 1 99 | tab_state[2].index = 1 100 | end 101 | 102 | M.back = function() 103 | M.tab_switch(1) 104 | tab_state[2].index = 1 105 | end 106 | 107 | return M 108 | -------------------------------------------------------------------------------- /lua/dbout/utils.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.generate_uuid = function() 4 | local random = math.random 5 | local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" 6 | return string.gsub(template, "[xy]", function(c) 7 | local v = (c == "x") and random(0, 0xf) or random(8, 0xb) 8 | return string.format("%x", v) 9 | end) 10 | end 11 | 12 | M.switch_win_to_buf = function(bufnr) 13 | local win = vim.fn.win_findbuf(bufnr) 14 | local winnr 15 | if #win > 0 then 16 | winnr = win[1] 17 | else 18 | winnr = vim.api.nvim_get_current_win() 19 | end 20 | 21 | vim.api.nvim_win_set_buf(winnr, bufnr) 22 | vim.api.nvim_set_current_win(winnr) 23 | end 24 | 25 | M.close_buf_win = function(bufnr) 26 | if bufnr and vim.api.nvim_buf_is_loaded(bufnr) then 27 | local wins = vim.fn.win_findbuf(bufnr) 28 | if #wins > 0 then 29 | vim.api.nvim_win_close(wins[1], true) 30 | end 31 | end 32 | end 33 | 34 | M.get_buf_win = function(bufnr) 35 | local wins = vim.fn.win_findbuf(bufnr) 36 | if #wins == 0 then 37 | return nil 38 | end 39 | return wins[1] 40 | end 41 | 42 | M.create_right_win = function() 43 | vim.cmd("botright vsplit") 44 | return vim.api.nvim_get_current_win() 45 | end 46 | 47 | M.get_or_create_buf_win = function(bufnr) 48 | local winnr = M.get_buf_win(bufnr) 49 | if not winnr then 50 | winnr = M.create_right_win() 51 | end 52 | return winnr 53 | end 54 | 55 | M.split_json = function(jsonstr) 56 | return vim.split(jsonstr, "\n", { plain = true }) 57 | end 58 | 59 | M.set_buf_lines = function(buf, lines) 60 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) 61 | end 62 | 63 | return M 64 | -------------------------------------------------------------------------------- /lua/telescope/_extensions/dbout.lua: -------------------------------------------------------------------------------- 1 | local telescope = require("telescope") 2 | local pickers = require("telescope.pickers") 3 | local finders = require("telescope.finders") 4 | local action_state = require("telescope.actions.state") 5 | local actions = require("telescope.actions") 6 | local conf = require("telescope.config").values 7 | local entry_display = require("telescope.pickers.entry_display") 8 | local conn = require("dbout.connection") 9 | local queryer = require("dbout.ui.queryer") 10 | local rpc = require("dbout.rpc") 11 | 12 | local options = { 13 | keymaps = { 14 | open_connection = "", 15 | new_connection = "n", 16 | delete_connection = "d", 17 | edit_connection = "e", 18 | attach_connection = "a", 19 | }, 20 | } 21 | 22 | local M = {} 23 | 24 | local function create_finder(connections) 25 | local displayer = entry_display.create({ 26 | separator = " ", 27 | items = { 28 | { 29 | width = 15, 30 | }, 31 | { 32 | remaining = true, 33 | }, 34 | }, 35 | }) 36 | 37 | local function make_display(entry) 38 | return displayer({ entry.name, { entry.value.db_type .. ":" .. entry.value.connstr, "Comment" } }) 39 | end 40 | 41 | return finders.new_table({ 42 | results = connections, 43 | entry_maker = function(entry) 44 | return { 45 | display = make_display, 46 | name = entry.name, 47 | value = entry, 48 | ordinal = entry.name, 49 | } 50 | end, 51 | }) 52 | end 53 | 54 | local new_picker = function() 55 | pickers 56 | .new({}, { 57 | prompt_title = "Connections", 58 | finder = create_finder(conn.get_connections()), 59 | sorter = conf.generic_sorter({}), 60 | attach_mappings = function(_, map) 61 | actions.select_default:replace(function() end) 62 | 63 | local key = options.keymaps 64 | map("n", key.open_connection, M.open_connection) 65 | map("n", key.new_connection, M.new_connection) 66 | map("n", key.delete_connection, M.delete_connection) 67 | map("n", key.edit_connection, M.edit_connection) 68 | map("n", key.attach_connection, M.attach_connection) 69 | 70 | return true 71 | end, 72 | }) 73 | :find() 74 | end 75 | 76 | local refresh_picker = function(prompt_bufnr) 77 | local picker = action_state.get_current_picker(prompt_bufnr) 78 | if picker then 79 | local finder = create_finder(conn.get_connections()) 80 | picker:refresh(finder) 81 | else 82 | M.open_connection_picker() 83 | end 84 | end 85 | 86 | M.open_connection_picker = function() 87 | if not rpc.is_alive() then 88 | rpc.server_up() 89 | end 90 | new_picker() 91 | end 92 | 93 | M.open_connection = function(prompt_bufnr) 94 | local selection = action_state.get_selected_entry() 95 | if not selection then 96 | return false 97 | end 98 | 99 | local connection = selection.value 100 | conn.open_connection(connection, function() 101 | actions.close(prompt_bufnr) 102 | queryer.create_buf(connection) 103 | end) 104 | end 105 | 106 | M.new_connection = function(prompt_bufnr) 107 | actions.close(prompt_bufnr) 108 | conn.create_connection({}, function(c) 109 | conn.add_connection(c) 110 | refresh_picker(prompt_bufnr) 111 | end) 112 | end 113 | 114 | M.delete_connection = function(prompt_bufnr) 115 | local connection = action_state.get_selected_entry().value 116 | conn.remove_connection(connection.id) 117 | refresh_picker(prompt_bufnr) 118 | end 119 | 120 | M.edit_connection = function(prompt_bufnr) 121 | local connection = action_state.get_selected_entry().value 122 | conn.create_connection(connection, function(c) 123 | conn.update_connection(c) 124 | refresh_picker(prompt_bufnr) 125 | end) 126 | end 127 | 128 | M.attach_connection = function(prompt_bufnr) 129 | local connection = action_state.get_selected_entry().value 130 | conn.open_connection(connection, function() 131 | actions.close(prompt_bufnr) 132 | queryer.attach_buf(connection, vim.api.nvim_get_current_buf()) 133 | end) 134 | end 135 | 136 | return telescope.register_extension({ 137 | setup = function(opts) 138 | options = vim.tbl_deep_extend("force", options, opts or {}) 139 | end, 140 | exports = { 141 | dbout = M.open_connection_picker, 142 | }, 143 | }) 144 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbout.nvim", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "better-sqlite3": "^12.4.1", 9 | "mssql": "^12.0.0", 10 | "mysql2": "^3.15.2", 11 | "pg": "^8.16.3", 12 | "sql-formatter": "^15.6.10" 13 | } 14 | }, 15 | "node_modules/@azure-rest/core-client": { 16 | "version": "2.5.1", 17 | "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz", 18 | "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", 19 | "license": "MIT", 20 | "dependencies": { 21 | "@azure/abort-controller": "^2.1.2", 22 | "@azure/core-auth": "^1.10.0", 23 | "@azure/core-rest-pipeline": "^1.22.0", 24 | "@azure/core-tracing": "^1.3.0", 25 | "@typespec/ts-http-runtime": "^0.3.0", 26 | "tslib": "^2.6.2" 27 | }, 28 | "engines": { 29 | "node": ">=20.0.0" 30 | } 31 | }, 32 | "node_modules/@azure/abort-controller": { 33 | "version": "2.1.2", 34 | "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", 35 | "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", 36 | "license": "MIT", 37 | "dependencies": { 38 | "tslib": "^2.6.2" 39 | }, 40 | "engines": { 41 | "node": ">=18.0.0" 42 | } 43 | }, 44 | "node_modules/@azure/core-auth": { 45 | "version": "1.10.1", 46 | "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", 47 | "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", 48 | "license": "MIT", 49 | "dependencies": { 50 | "@azure/abort-controller": "^2.1.2", 51 | "@azure/core-util": "^1.13.0", 52 | "tslib": "^2.6.2" 53 | }, 54 | "engines": { 55 | "node": ">=20.0.0" 56 | } 57 | }, 58 | "node_modules/@azure/core-client": { 59 | "version": "1.10.1", 60 | "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", 61 | "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", 62 | "license": "MIT", 63 | "dependencies": { 64 | "@azure/abort-controller": "^2.1.2", 65 | "@azure/core-auth": "^1.10.0", 66 | "@azure/core-rest-pipeline": "^1.22.0", 67 | "@azure/core-tracing": "^1.3.0", 68 | "@azure/core-util": "^1.13.0", 69 | "@azure/logger": "^1.3.0", 70 | "tslib": "^2.6.2" 71 | }, 72 | "engines": { 73 | "node": ">=20.0.0" 74 | } 75 | }, 76 | "node_modules/@azure/core-http-compat": { 77 | "version": "2.3.1", 78 | "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz", 79 | "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==", 80 | "license": "MIT", 81 | "dependencies": { 82 | "@azure/abort-controller": "^2.1.2", 83 | "@azure/core-client": "^1.10.0", 84 | "@azure/core-rest-pipeline": "^1.22.0" 85 | }, 86 | "engines": { 87 | "node": ">=20.0.0" 88 | } 89 | }, 90 | "node_modules/@azure/core-lro": { 91 | "version": "2.7.2", 92 | "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", 93 | "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", 94 | "license": "MIT", 95 | "dependencies": { 96 | "@azure/abort-controller": "^2.0.0", 97 | "@azure/core-util": "^1.2.0", 98 | "@azure/logger": "^1.0.0", 99 | "tslib": "^2.6.2" 100 | }, 101 | "engines": { 102 | "node": ">=18.0.0" 103 | } 104 | }, 105 | "node_modules/@azure/core-paging": { 106 | "version": "1.6.2", 107 | "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", 108 | "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", 109 | "license": "MIT", 110 | "dependencies": { 111 | "tslib": "^2.6.2" 112 | }, 113 | "engines": { 114 | "node": ">=18.0.0" 115 | } 116 | }, 117 | "node_modules/@azure/core-rest-pipeline": { 118 | "version": "1.22.1", 119 | "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.1.tgz", 120 | "integrity": "sha512-UVZlVLfLyz6g3Hy7GNDpooMQonUygH7ghdiSASOOHy97fKj/mPLqgDX7aidOijn+sCMU+WU8NjlPlNTgnvbcGA==", 121 | "license": "MIT", 122 | "dependencies": { 123 | "@azure/abort-controller": "^2.1.2", 124 | "@azure/core-auth": "^1.10.0", 125 | "@azure/core-tracing": "^1.3.0", 126 | "@azure/core-util": "^1.13.0", 127 | "@azure/logger": "^1.3.0", 128 | "@typespec/ts-http-runtime": "^0.3.0", 129 | "tslib": "^2.6.2" 130 | }, 131 | "engines": { 132 | "node": ">=20.0.0" 133 | } 134 | }, 135 | "node_modules/@azure/core-tracing": { 136 | "version": "1.3.1", 137 | "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", 138 | "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", 139 | "license": "MIT", 140 | "dependencies": { 141 | "tslib": "^2.6.2" 142 | }, 143 | "engines": { 144 | "node": ">=20.0.0" 145 | } 146 | }, 147 | "node_modules/@azure/core-util": { 148 | "version": "1.13.1", 149 | "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", 150 | "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", 151 | "license": "MIT", 152 | "dependencies": { 153 | "@azure/abort-controller": "^2.1.2", 154 | "@typespec/ts-http-runtime": "^0.3.0", 155 | "tslib": "^2.6.2" 156 | }, 157 | "engines": { 158 | "node": ">=20.0.0" 159 | } 160 | }, 161 | "node_modules/@azure/identity": { 162 | "version": "4.13.0", 163 | "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", 164 | "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", 165 | "license": "MIT", 166 | "dependencies": { 167 | "@azure/abort-controller": "^2.0.0", 168 | "@azure/core-auth": "^1.9.0", 169 | "@azure/core-client": "^1.9.2", 170 | "@azure/core-rest-pipeline": "^1.17.0", 171 | "@azure/core-tracing": "^1.0.0", 172 | "@azure/core-util": "^1.11.0", 173 | "@azure/logger": "^1.0.0", 174 | "@azure/msal-browser": "^4.2.0", 175 | "@azure/msal-node": "^3.5.0", 176 | "open": "^10.1.0", 177 | "tslib": "^2.2.0" 178 | }, 179 | "engines": { 180 | "node": ">=20.0.0" 181 | } 182 | }, 183 | "node_modules/@azure/keyvault-common": { 184 | "version": "2.0.0", 185 | "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", 186 | "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", 187 | "license": "MIT", 188 | "dependencies": { 189 | "@azure/abort-controller": "^2.0.0", 190 | "@azure/core-auth": "^1.3.0", 191 | "@azure/core-client": "^1.5.0", 192 | "@azure/core-rest-pipeline": "^1.8.0", 193 | "@azure/core-tracing": "^1.0.0", 194 | "@azure/core-util": "^1.10.0", 195 | "@azure/logger": "^1.1.4", 196 | "tslib": "^2.2.0" 197 | }, 198 | "engines": { 199 | "node": ">=18.0.0" 200 | } 201 | }, 202 | "node_modules/@azure/keyvault-keys": { 203 | "version": "4.10.0", 204 | "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", 205 | "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", 206 | "license": "MIT", 207 | "dependencies": { 208 | "@azure-rest/core-client": "^2.3.3", 209 | "@azure/abort-controller": "^2.1.2", 210 | "@azure/core-auth": "^1.9.0", 211 | "@azure/core-http-compat": "^2.2.0", 212 | "@azure/core-lro": "^2.7.2", 213 | "@azure/core-paging": "^1.6.2", 214 | "@azure/core-rest-pipeline": "^1.19.0", 215 | "@azure/core-tracing": "^1.2.0", 216 | "@azure/core-util": "^1.11.0", 217 | "@azure/keyvault-common": "^2.0.0", 218 | "@azure/logger": "^1.1.4", 219 | "tslib": "^2.8.1" 220 | }, 221 | "engines": { 222 | "node": ">=18.0.0" 223 | } 224 | }, 225 | "node_modules/@azure/logger": { 226 | "version": "1.3.0", 227 | "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", 228 | "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", 229 | "license": "MIT", 230 | "dependencies": { 231 | "@typespec/ts-http-runtime": "^0.3.0", 232 | "tslib": "^2.6.2" 233 | }, 234 | "engines": { 235 | "node": ">=20.0.0" 236 | } 237 | }, 238 | "node_modules/@azure/msal-browser": { 239 | "version": "4.25.0", 240 | "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.25.0.tgz", 241 | "integrity": "sha512-kbL+Ae7/UC62wSzxirZddYeVnHvvkvAnSZkBqL55X+jaSXTAXfngnNsDM5acEWU0Q/SAv3gEQfxO1igWOn87Pg==", 242 | "license": "MIT", 243 | "dependencies": { 244 | "@azure/msal-common": "15.13.0" 245 | }, 246 | "engines": { 247 | "node": ">=0.8.0" 248 | } 249 | }, 250 | "node_modules/@azure/msal-common": { 251 | "version": "15.13.0", 252 | "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.0.tgz", 253 | "integrity": "sha512-8oF6nj02qX7eE/6+wFT5NluXRHc05AgdCC3fJnkjiJooq8u7BcLmxaYYSwc2AfEkWRMRi6Eyvvbeqk4U4412Ag==", 254 | "license": "MIT", 255 | "engines": { 256 | "node": ">=0.8.0" 257 | } 258 | }, 259 | "node_modules/@azure/msal-node": { 260 | "version": "3.8.0", 261 | "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.0.tgz", 262 | "integrity": "sha512-23BXm82Mp5XnRhrcd4mrHa0xuUNRp96ivu3nRatrfdAqjoeWAGyD0eEAafxAOHAEWWmdlyFK4ELFcdziXyw2sA==", 263 | "license": "MIT", 264 | "dependencies": { 265 | "@azure/msal-common": "15.13.0", 266 | "jsonwebtoken": "^9.0.0", 267 | "uuid": "^8.3.0" 268 | }, 269 | "engines": { 270 | "node": ">=16" 271 | } 272 | }, 273 | "node_modules/@js-joda/core": { 274 | "version": "5.6.5", 275 | "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz", 276 | "integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==", 277 | "license": "BSD-3-Clause" 278 | }, 279 | "node_modules/@tediousjs/connection-string": { 280 | "version": "0.6.0", 281 | "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.6.0.tgz", 282 | "integrity": "sha512-GxlsW354Vi6QqbUgdPyQVcQjI7cZBdGV5vOYVYuCVDTylx2wl3WHR2HlhcxxHTrMigbelpXsdcZso+66uxPfow==", 283 | "license": "MIT" 284 | }, 285 | "node_modules/@types/node": { 286 | "version": "24.7.0", 287 | "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", 288 | "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", 289 | "license": "MIT", 290 | "dependencies": { 291 | "undici-types": "~7.14.0" 292 | } 293 | }, 294 | "node_modules/@types/readable-stream": { 295 | "version": "4.0.21", 296 | "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", 297 | "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", 298 | "license": "MIT", 299 | "dependencies": { 300 | "@types/node": "*" 301 | } 302 | }, 303 | "node_modules/@typespec/ts-http-runtime": { 304 | "version": "0.3.1", 305 | "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.1.tgz", 306 | "integrity": "sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww==", 307 | "license": "MIT", 308 | "dependencies": { 309 | "http-proxy-agent": "^7.0.0", 310 | "https-proxy-agent": "^7.0.0", 311 | "tslib": "^2.6.2" 312 | }, 313 | "engines": { 314 | "node": ">=20.0.0" 315 | } 316 | }, 317 | "node_modules/abort-controller": { 318 | "version": "3.0.0", 319 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 320 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 321 | "license": "MIT", 322 | "dependencies": { 323 | "event-target-shim": "^5.0.0" 324 | }, 325 | "engines": { 326 | "node": ">=6.5" 327 | } 328 | }, 329 | "node_modules/agent-base": { 330 | "version": "7.1.4", 331 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", 332 | "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", 333 | "license": "MIT", 334 | "engines": { 335 | "node": ">= 14" 336 | } 337 | }, 338 | "node_modules/argparse": { 339 | "version": "2.0.1", 340 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 341 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 342 | "license": "Python-2.0" 343 | }, 344 | "node_modules/aws-ssl-profiles": { 345 | "version": "1.1.2", 346 | "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", 347 | "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", 348 | "license": "MIT", 349 | "engines": { 350 | "node": ">= 6.0.0" 351 | } 352 | }, 353 | "node_modules/base64-js": { 354 | "version": "1.5.1", 355 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 356 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 357 | "funding": [ 358 | { 359 | "type": "github", 360 | "url": "https://github.com/sponsors/feross" 361 | }, 362 | { 363 | "type": "patreon", 364 | "url": "https://www.patreon.com/feross" 365 | }, 366 | { 367 | "type": "consulting", 368 | "url": "https://feross.org/support" 369 | } 370 | ], 371 | "license": "MIT" 372 | }, 373 | "node_modules/better-sqlite3": { 374 | "version": "12.4.1", 375 | "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.4.1.tgz", 376 | "integrity": "sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==", 377 | "hasInstallScript": true, 378 | "license": "MIT", 379 | "dependencies": { 380 | "bindings": "^1.5.0", 381 | "prebuild-install": "^7.1.1" 382 | }, 383 | "engines": { 384 | "node": "20.x || 22.x || 23.x || 24.x" 385 | } 386 | }, 387 | "node_modules/bindings": { 388 | "version": "1.5.0", 389 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 390 | "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 391 | "license": "MIT", 392 | "dependencies": { 393 | "file-uri-to-path": "1.0.0" 394 | } 395 | }, 396 | "node_modules/bl": { 397 | "version": "6.1.3", 398 | "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.3.tgz", 399 | "integrity": "sha512-nHB8B5roHlGX5TFsWeiQJijdddZIOHuv1eL2cM2kHnG3qR91CYLsysGe+CvxQfEd23EKD0eJf4lto0frTbddKA==", 400 | "license": "MIT", 401 | "dependencies": { 402 | "@types/readable-stream": "^4.0.0", 403 | "buffer": "^6.0.3", 404 | "inherits": "^2.0.4", 405 | "readable-stream": "^4.2.0" 406 | } 407 | }, 408 | "node_modules/buffer": { 409 | "version": "6.0.3", 410 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 411 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 412 | "funding": [ 413 | { 414 | "type": "github", 415 | "url": "https://github.com/sponsors/feross" 416 | }, 417 | { 418 | "type": "patreon", 419 | "url": "https://www.patreon.com/feross" 420 | }, 421 | { 422 | "type": "consulting", 423 | "url": "https://feross.org/support" 424 | } 425 | ], 426 | "license": "MIT", 427 | "dependencies": { 428 | "base64-js": "^1.3.1", 429 | "ieee754": "^1.2.1" 430 | } 431 | }, 432 | "node_modules/buffer-equal-constant-time": { 433 | "version": "1.0.1", 434 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 435 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", 436 | "license": "BSD-3-Clause" 437 | }, 438 | "node_modules/bundle-name": { 439 | "version": "4.1.0", 440 | "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", 441 | "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", 442 | "license": "MIT", 443 | "dependencies": { 444 | "run-applescript": "^7.0.0" 445 | }, 446 | "engines": { 447 | "node": ">=18" 448 | }, 449 | "funding": { 450 | "url": "https://github.com/sponsors/sindresorhus" 451 | } 452 | }, 453 | "node_modules/chownr": { 454 | "version": "1.1.4", 455 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 456 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 457 | "license": "ISC" 458 | }, 459 | "node_modules/commander": { 460 | "version": "11.1.0", 461 | "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", 462 | "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", 463 | "license": "MIT", 464 | "engines": { 465 | "node": ">=16" 466 | } 467 | }, 468 | "node_modules/debug": { 469 | "version": "4.4.3", 470 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 471 | "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 472 | "license": "MIT", 473 | "dependencies": { 474 | "ms": "^2.1.3" 475 | }, 476 | "engines": { 477 | "node": ">=6.0" 478 | }, 479 | "peerDependenciesMeta": { 480 | "supports-color": { 481 | "optional": true 482 | } 483 | } 484 | }, 485 | "node_modules/decompress-response": { 486 | "version": "6.0.0", 487 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 488 | "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 489 | "license": "MIT", 490 | "dependencies": { 491 | "mimic-response": "^3.1.0" 492 | }, 493 | "engines": { 494 | "node": ">=10" 495 | }, 496 | "funding": { 497 | "url": "https://github.com/sponsors/sindresorhus" 498 | } 499 | }, 500 | "node_modules/deep-extend": { 501 | "version": "0.6.0", 502 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 503 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 504 | "license": "MIT", 505 | "engines": { 506 | "node": ">=4.0.0" 507 | } 508 | }, 509 | "node_modules/default-browser": { 510 | "version": "5.2.1", 511 | "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", 512 | "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", 513 | "license": "MIT", 514 | "dependencies": { 515 | "bundle-name": "^4.1.0", 516 | "default-browser-id": "^5.0.0" 517 | }, 518 | "engines": { 519 | "node": ">=18" 520 | }, 521 | "funding": { 522 | "url": "https://github.com/sponsors/sindresorhus" 523 | } 524 | }, 525 | "node_modules/default-browser-id": { 526 | "version": "5.0.0", 527 | "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", 528 | "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", 529 | "license": "MIT", 530 | "engines": { 531 | "node": ">=18" 532 | }, 533 | "funding": { 534 | "url": "https://github.com/sponsors/sindresorhus" 535 | } 536 | }, 537 | "node_modules/define-lazy-prop": { 538 | "version": "3.0.0", 539 | "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", 540 | "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", 541 | "license": "MIT", 542 | "engines": { 543 | "node": ">=12" 544 | }, 545 | "funding": { 546 | "url": "https://github.com/sponsors/sindresorhus" 547 | } 548 | }, 549 | "node_modules/denque": { 550 | "version": "2.1.0", 551 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", 552 | "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", 553 | "license": "Apache-2.0", 554 | "engines": { 555 | "node": ">=0.10" 556 | } 557 | }, 558 | "node_modules/detect-libc": { 559 | "version": "2.1.0", 560 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", 561 | "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", 562 | "license": "Apache-2.0", 563 | "engines": { 564 | "node": ">=8" 565 | } 566 | }, 567 | "node_modules/discontinuous-range": { 568 | "version": "1.0.0", 569 | "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", 570 | "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", 571 | "license": "MIT" 572 | }, 573 | "node_modules/ecdsa-sig-formatter": { 574 | "version": "1.0.11", 575 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 576 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 577 | "license": "Apache-2.0", 578 | "dependencies": { 579 | "safe-buffer": "^5.0.1" 580 | } 581 | }, 582 | "node_modules/end-of-stream": { 583 | "version": "1.4.5", 584 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", 585 | "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", 586 | "license": "MIT", 587 | "dependencies": { 588 | "once": "^1.4.0" 589 | } 590 | }, 591 | "node_modules/event-target-shim": { 592 | "version": "5.0.1", 593 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 594 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 595 | "license": "MIT", 596 | "engines": { 597 | "node": ">=6" 598 | } 599 | }, 600 | "node_modules/events": { 601 | "version": "3.3.0", 602 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 603 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 604 | "license": "MIT", 605 | "engines": { 606 | "node": ">=0.8.x" 607 | } 608 | }, 609 | "node_modules/expand-template": { 610 | "version": "2.0.3", 611 | "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 612 | "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", 613 | "license": "(MIT OR WTFPL)", 614 | "engines": { 615 | "node": ">=6" 616 | } 617 | }, 618 | "node_modules/file-uri-to-path": { 619 | "version": "1.0.0", 620 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 621 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 622 | "license": "MIT" 623 | }, 624 | "node_modules/fs-constants": { 625 | "version": "1.0.0", 626 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 627 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 628 | "license": "MIT" 629 | }, 630 | "node_modules/generate-function": { 631 | "version": "2.3.1", 632 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", 633 | "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", 634 | "license": "MIT", 635 | "dependencies": { 636 | "is-property": "^1.0.2" 637 | } 638 | }, 639 | "node_modules/github-from-package": { 640 | "version": "0.0.0", 641 | "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 642 | "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 643 | "license": "MIT" 644 | }, 645 | "node_modules/http-proxy-agent": { 646 | "version": "7.0.2", 647 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", 648 | "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 649 | "license": "MIT", 650 | "dependencies": { 651 | "agent-base": "^7.1.0", 652 | "debug": "^4.3.4" 653 | }, 654 | "engines": { 655 | "node": ">= 14" 656 | } 657 | }, 658 | "node_modules/https-proxy-agent": { 659 | "version": "7.0.6", 660 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", 661 | "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", 662 | "license": "MIT", 663 | "dependencies": { 664 | "agent-base": "^7.1.2", 665 | "debug": "4" 666 | }, 667 | "engines": { 668 | "node": ">= 14" 669 | } 670 | }, 671 | "node_modules/iconv-lite": { 672 | "version": "0.6.3", 673 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 674 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 675 | "license": "MIT", 676 | "dependencies": { 677 | "safer-buffer": ">= 2.1.2 < 3.0.0" 678 | }, 679 | "engines": { 680 | "node": ">=0.10.0" 681 | } 682 | }, 683 | "node_modules/ieee754": { 684 | "version": "1.2.1", 685 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 686 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 687 | "funding": [ 688 | { 689 | "type": "github", 690 | "url": "https://github.com/sponsors/feross" 691 | }, 692 | { 693 | "type": "patreon", 694 | "url": "https://www.patreon.com/feross" 695 | }, 696 | { 697 | "type": "consulting", 698 | "url": "https://feross.org/support" 699 | } 700 | ], 701 | "license": "BSD-3-Clause" 702 | }, 703 | "node_modules/inherits": { 704 | "version": "2.0.4", 705 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 706 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 707 | "license": "ISC" 708 | }, 709 | "node_modules/ini": { 710 | "version": "1.3.8", 711 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 712 | "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 713 | "license": "ISC" 714 | }, 715 | "node_modules/is-docker": { 716 | "version": "3.0.0", 717 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", 718 | "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", 719 | "license": "MIT", 720 | "bin": { 721 | "is-docker": "cli.js" 722 | }, 723 | "engines": { 724 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 725 | }, 726 | "funding": { 727 | "url": "https://github.com/sponsors/sindresorhus" 728 | } 729 | }, 730 | "node_modules/is-inside-container": { 731 | "version": "1.0.0", 732 | "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", 733 | "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", 734 | "license": "MIT", 735 | "dependencies": { 736 | "is-docker": "^3.0.0" 737 | }, 738 | "bin": { 739 | "is-inside-container": "cli.js" 740 | }, 741 | "engines": { 742 | "node": ">=14.16" 743 | }, 744 | "funding": { 745 | "url": "https://github.com/sponsors/sindresorhus" 746 | } 747 | }, 748 | "node_modules/is-property": { 749 | "version": "1.0.2", 750 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 751 | "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", 752 | "license": "MIT" 753 | }, 754 | "node_modules/is-wsl": { 755 | "version": "3.1.0", 756 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", 757 | "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", 758 | "license": "MIT", 759 | "dependencies": { 760 | "is-inside-container": "^1.0.0" 761 | }, 762 | "engines": { 763 | "node": ">=16" 764 | }, 765 | "funding": { 766 | "url": "https://github.com/sponsors/sindresorhus" 767 | } 768 | }, 769 | "node_modules/js-md4": { 770 | "version": "0.3.2", 771 | "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", 772 | "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", 773 | "license": "MIT" 774 | }, 775 | "node_modules/jsonwebtoken": { 776 | "version": "9.0.2", 777 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", 778 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", 779 | "license": "MIT", 780 | "dependencies": { 781 | "jws": "^3.2.2", 782 | "lodash.includes": "^4.3.0", 783 | "lodash.isboolean": "^3.0.3", 784 | "lodash.isinteger": "^4.0.4", 785 | "lodash.isnumber": "^3.0.3", 786 | "lodash.isplainobject": "^4.0.6", 787 | "lodash.isstring": "^4.0.1", 788 | "lodash.once": "^4.0.0", 789 | "ms": "^2.1.1", 790 | "semver": "^7.5.4" 791 | }, 792 | "engines": { 793 | "node": ">=12", 794 | "npm": ">=6" 795 | } 796 | }, 797 | "node_modules/jwa": { 798 | "version": "1.4.2", 799 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", 800 | "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", 801 | "license": "MIT", 802 | "dependencies": { 803 | "buffer-equal-constant-time": "^1.0.1", 804 | "ecdsa-sig-formatter": "1.0.11", 805 | "safe-buffer": "^5.0.1" 806 | } 807 | }, 808 | "node_modules/jws": { 809 | "version": "3.2.2", 810 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 811 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 812 | "license": "MIT", 813 | "dependencies": { 814 | "jwa": "^1.4.1", 815 | "safe-buffer": "^5.0.1" 816 | } 817 | }, 818 | "node_modules/lodash.includes": { 819 | "version": "4.3.0", 820 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 821 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", 822 | "license": "MIT" 823 | }, 824 | "node_modules/lodash.isboolean": { 825 | "version": "3.0.3", 826 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 827 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", 828 | "license": "MIT" 829 | }, 830 | "node_modules/lodash.isinteger": { 831 | "version": "4.0.4", 832 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 833 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", 834 | "license": "MIT" 835 | }, 836 | "node_modules/lodash.isnumber": { 837 | "version": "3.0.3", 838 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 839 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", 840 | "license": "MIT" 841 | }, 842 | "node_modules/lodash.isplainobject": { 843 | "version": "4.0.6", 844 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 845 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", 846 | "license": "MIT" 847 | }, 848 | "node_modules/lodash.isstring": { 849 | "version": "4.0.1", 850 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 851 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", 852 | "license": "MIT" 853 | }, 854 | "node_modules/lodash.once": { 855 | "version": "4.1.1", 856 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 857 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", 858 | "license": "MIT" 859 | }, 860 | "node_modules/long": { 861 | "version": "5.3.2", 862 | "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", 863 | "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", 864 | "license": "Apache-2.0" 865 | }, 866 | "node_modules/lru-cache": { 867 | "version": "7.18.3", 868 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", 869 | "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", 870 | "license": "ISC", 871 | "engines": { 872 | "node": ">=12" 873 | } 874 | }, 875 | "node_modules/lru.min": { 876 | "version": "1.1.2", 877 | "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", 878 | "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", 879 | "license": "MIT", 880 | "engines": { 881 | "bun": ">=1.0.0", 882 | "deno": ">=1.30.0", 883 | "node": ">=8.0.0" 884 | }, 885 | "funding": { 886 | "type": "github", 887 | "url": "https://github.com/sponsors/wellwelwel" 888 | } 889 | }, 890 | "node_modules/mimic-response": { 891 | "version": "3.1.0", 892 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 893 | "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 894 | "license": "MIT", 895 | "engines": { 896 | "node": ">=10" 897 | }, 898 | "funding": { 899 | "url": "https://github.com/sponsors/sindresorhus" 900 | } 901 | }, 902 | "node_modules/minimist": { 903 | "version": "1.2.8", 904 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 905 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 906 | "license": "MIT", 907 | "funding": { 908 | "url": "https://github.com/sponsors/ljharb" 909 | } 910 | }, 911 | "node_modules/mkdirp-classic": { 912 | "version": "0.5.3", 913 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 914 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 915 | "license": "MIT" 916 | }, 917 | "node_modules/moo": { 918 | "version": "0.5.2", 919 | "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", 920 | "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", 921 | "license": "BSD-3-Clause" 922 | }, 923 | "node_modules/ms": { 924 | "version": "2.1.3", 925 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 926 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 927 | "license": "MIT" 928 | }, 929 | "node_modules/mssql": { 930 | "version": "12.0.0", 931 | "resolved": "https://registry.npmjs.org/mssql/-/mssql-12.0.0.tgz", 932 | "integrity": "sha512-FcDQ1Gwe4g3Mhw25R1Onr8N+jmqBTWE/pmtcgxYnAUSIf/vBQMvJfMnyMY8ruOICtBch5+Wgbcfd3REDQSlWpA==", 933 | "license": "MIT", 934 | "dependencies": { 935 | "@tediousjs/connection-string": "^0.6.0", 936 | "commander": "^11.0.0", 937 | "debug": "^4.3.3", 938 | "tarn": "^3.0.2", 939 | "tedious": "^19.0.0" 940 | }, 941 | "bin": { 942 | "mssql": "bin/mssql" 943 | }, 944 | "engines": { 945 | "node": ">=18" 946 | } 947 | }, 948 | "node_modules/mysql2": { 949 | "version": "3.15.2", 950 | "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.2.tgz", 951 | "integrity": "sha512-kFm5+jbwR5mC+lo+3Cy46eHiykWSpUtTLOH3GE+AR7GeLq8PgfJcvpMiyVWk9/O53DjQsqm6a3VOOfq7gYWFRg==", 952 | "license": "MIT", 953 | "dependencies": { 954 | "aws-ssl-profiles": "^1.1.1", 955 | "denque": "^2.1.0", 956 | "generate-function": "^2.3.1", 957 | "iconv-lite": "^0.7.0", 958 | "long": "^5.2.1", 959 | "lru.min": "^1.0.0", 960 | "named-placeholders": "^1.1.3", 961 | "seq-queue": "^0.0.5", 962 | "sqlstring": "^2.3.2" 963 | }, 964 | "engines": { 965 | "node": ">= 8.0" 966 | } 967 | }, 968 | "node_modules/mysql2/node_modules/iconv-lite": { 969 | "version": "0.7.0", 970 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", 971 | "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", 972 | "license": "MIT", 973 | "dependencies": { 974 | "safer-buffer": ">= 2.1.2 < 3.0.0" 975 | }, 976 | "engines": { 977 | "node": ">=0.10.0" 978 | }, 979 | "funding": { 980 | "type": "opencollective", 981 | "url": "https://opencollective.com/express" 982 | } 983 | }, 984 | "node_modules/named-placeholders": { 985 | "version": "1.1.3", 986 | "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", 987 | "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", 988 | "license": "MIT", 989 | "dependencies": { 990 | "lru-cache": "^7.14.1" 991 | }, 992 | "engines": { 993 | "node": ">=12.0.0" 994 | } 995 | }, 996 | "node_modules/napi-build-utils": { 997 | "version": "2.0.0", 998 | "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", 999 | "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", 1000 | "license": "MIT" 1001 | }, 1002 | "node_modules/native-duplexpair": { 1003 | "version": "1.0.0", 1004 | "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", 1005 | "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", 1006 | "license": "MIT" 1007 | }, 1008 | "node_modules/nearley": { 1009 | "version": "2.20.1", 1010 | "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", 1011 | "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", 1012 | "license": "MIT", 1013 | "dependencies": { 1014 | "commander": "^2.19.0", 1015 | "moo": "^0.5.0", 1016 | "railroad-diagrams": "^1.0.0", 1017 | "randexp": "0.4.6" 1018 | }, 1019 | "bin": { 1020 | "nearley-railroad": "bin/nearley-railroad.js", 1021 | "nearley-test": "bin/nearley-test.js", 1022 | "nearley-unparse": "bin/nearley-unparse.js", 1023 | "nearleyc": "bin/nearleyc.js" 1024 | }, 1025 | "funding": { 1026 | "type": "individual", 1027 | "url": "https://nearley.js.org/#give-to-nearley" 1028 | } 1029 | }, 1030 | "node_modules/nearley/node_modules/commander": { 1031 | "version": "2.20.3", 1032 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 1033 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 1034 | "license": "MIT" 1035 | }, 1036 | "node_modules/node-abi": { 1037 | "version": "3.77.0", 1038 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", 1039 | "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==", 1040 | "license": "MIT", 1041 | "dependencies": { 1042 | "semver": "^7.3.5" 1043 | }, 1044 | "engines": { 1045 | "node": ">=10" 1046 | } 1047 | }, 1048 | "node_modules/once": { 1049 | "version": "1.4.0", 1050 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1051 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1052 | "license": "ISC", 1053 | "dependencies": { 1054 | "wrappy": "1" 1055 | } 1056 | }, 1057 | "node_modules/open": { 1058 | "version": "10.2.0", 1059 | "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", 1060 | "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", 1061 | "license": "MIT", 1062 | "dependencies": { 1063 | "default-browser": "^5.2.1", 1064 | "define-lazy-prop": "^3.0.0", 1065 | "is-inside-container": "^1.0.0", 1066 | "wsl-utils": "^0.1.0" 1067 | }, 1068 | "engines": { 1069 | "node": ">=18" 1070 | }, 1071 | "funding": { 1072 | "url": "https://github.com/sponsors/sindresorhus" 1073 | } 1074 | }, 1075 | "node_modules/pg": { 1076 | "version": "8.16.3", 1077 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", 1078 | "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", 1079 | "license": "MIT", 1080 | "dependencies": { 1081 | "pg-connection-string": "^2.9.1", 1082 | "pg-pool": "^3.10.1", 1083 | "pg-protocol": "^1.10.3", 1084 | "pg-types": "2.2.0", 1085 | "pgpass": "1.0.5" 1086 | }, 1087 | "engines": { 1088 | "node": ">= 16.0.0" 1089 | }, 1090 | "optionalDependencies": { 1091 | "pg-cloudflare": "^1.2.7" 1092 | }, 1093 | "peerDependencies": { 1094 | "pg-native": ">=3.0.1" 1095 | }, 1096 | "peerDependenciesMeta": { 1097 | "pg-native": { 1098 | "optional": true 1099 | } 1100 | } 1101 | }, 1102 | "node_modules/pg-cloudflare": { 1103 | "version": "1.2.7", 1104 | "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", 1105 | "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", 1106 | "license": "MIT", 1107 | "optional": true 1108 | }, 1109 | "node_modules/pg-connection-string": { 1110 | "version": "2.9.1", 1111 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", 1112 | "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", 1113 | "license": "MIT" 1114 | }, 1115 | "node_modules/pg-int8": { 1116 | "version": "1.0.1", 1117 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 1118 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", 1119 | "license": "ISC", 1120 | "engines": { 1121 | "node": ">=4.0.0" 1122 | } 1123 | }, 1124 | "node_modules/pg-pool": { 1125 | "version": "3.10.1", 1126 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", 1127 | "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", 1128 | "license": "MIT", 1129 | "peerDependencies": { 1130 | "pg": ">=8.0" 1131 | } 1132 | }, 1133 | "node_modules/pg-protocol": { 1134 | "version": "1.10.3", 1135 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", 1136 | "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", 1137 | "license": "MIT" 1138 | }, 1139 | "node_modules/pg-types": { 1140 | "version": "2.2.0", 1141 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 1142 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 1143 | "license": "MIT", 1144 | "dependencies": { 1145 | "pg-int8": "1.0.1", 1146 | "postgres-array": "~2.0.0", 1147 | "postgres-bytea": "~1.0.0", 1148 | "postgres-date": "~1.0.4", 1149 | "postgres-interval": "^1.1.0" 1150 | }, 1151 | "engines": { 1152 | "node": ">=4" 1153 | } 1154 | }, 1155 | "node_modules/pgpass": { 1156 | "version": "1.0.5", 1157 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", 1158 | "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", 1159 | "license": "MIT", 1160 | "dependencies": { 1161 | "split2": "^4.1.0" 1162 | } 1163 | }, 1164 | "node_modules/postgres-array": { 1165 | "version": "2.0.0", 1166 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 1167 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", 1168 | "license": "MIT", 1169 | "engines": { 1170 | "node": ">=4" 1171 | } 1172 | }, 1173 | "node_modules/postgres-bytea": { 1174 | "version": "1.0.0", 1175 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 1176 | "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", 1177 | "license": "MIT", 1178 | "engines": { 1179 | "node": ">=0.10.0" 1180 | } 1181 | }, 1182 | "node_modules/postgres-date": { 1183 | "version": "1.0.7", 1184 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 1185 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", 1186 | "license": "MIT", 1187 | "engines": { 1188 | "node": ">=0.10.0" 1189 | } 1190 | }, 1191 | "node_modules/postgres-interval": { 1192 | "version": "1.2.0", 1193 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 1194 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 1195 | "license": "MIT", 1196 | "dependencies": { 1197 | "xtend": "^4.0.0" 1198 | }, 1199 | "engines": { 1200 | "node": ">=0.10.0" 1201 | } 1202 | }, 1203 | "node_modules/prebuild-install": { 1204 | "version": "7.1.3", 1205 | "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", 1206 | "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", 1207 | "license": "MIT", 1208 | "dependencies": { 1209 | "detect-libc": "^2.0.0", 1210 | "expand-template": "^2.0.3", 1211 | "github-from-package": "0.0.0", 1212 | "minimist": "^1.2.3", 1213 | "mkdirp-classic": "^0.5.3", 1214 | "napi-build-utils": "^2.0.0", 1215 | "node-abi": "^3.3.0", 1216 | "pump": "^3.0.0", 1217 | "rc": "^1.2.7", 1218 | "simple-get": "^4.0.0", 1219 | "tar-fs": "^2.0.0", 1220 | "tunnel-agent": "^0.6.0" 1221 | }, 1222 | "bin": { 1223 | "prebuild-install": "bin.js" 1224 | }, 1225 | "engines": { 1226 | "node": ">=10" 1227 | } 1228 | }, 1229 | "node_modules/process": { 1230 | "version": "0.11.10", 1231 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 1232 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 1233 | "license": "MIT", 1234 | "engines": { 1235 | "node": ">= 0.6.0" 1236 | } 1237 | }, 1238 | "node_modules/pump": { 1239 | "version": "3.0.3", 1240 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", 1241 | "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", 1242 | "license": "MIT", 1243 | "dependencies": { 1244 | "end-of-stream": "^1.1.0", 1245 | "once": "^1.3.1" 1246 | } 1247 | }, 1248 | "node_modules/railroad-diagrams": { 1249 | "version": "1.0.0", 1250 | "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", 1251 | "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", 1252 | "license": "CC0-1.0" 1253 | }, 1254 | "node_modules/randexp": { 1255 | "version": "0.4.6", 1256 | "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", 1257 | "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", 1258 | "license": "MIT", 1259 | "dependencies": { 1260 | "discontinuous-range": "1.0.0", 1261 | "ret": "~0.1.10" 1262 | }, 1263 | "engines": { 1264 | "node": ">=0.12" 1265 | } 1266 | }, 1267 | "node_modules/rc": { 1268 | "version": "1.2.8", 1269 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1270 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1271 | "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 1272 | "dependencies": { 1273 | "deep-extend": "^0.6.0", 1274 | "ini": "~1.3.0", 1275 | "minimist": "^1.2.0", 1276 | "strip-json-comments": "~2.0.1" 1277 | }, 1278 | "bin": { 1279 | "rc": "cli.js" 1280 | } 1281 | }, 1282 | "node_modules/readable-stream": { 1283 | "version": "4.7.0", 1284 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 1285 | "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 1286 | "license": "MIT", 1287 | "dependencies": { 1288 | "abort-controller": "^3.0.0", 1289 | "buffer": "^6.0.3", 1290 | "events": "^3.3.0", 1291 | "process": "^0.11.10", 1292 | "string_decoder": "^1.3.0" 1293 | }, 1294 | "engines": { 1295 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1296 | } 1297 | }, 1298 | "node_modules/ret": { 1299 | "version": "0.1.15", 1300 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", 1301 | "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", 1302 | "license": "MIT", 1303 | "engines": { 1304 | "node": ">=0.12" 1305 | } 1306 | }, 1307 | "node_modules/run-applescript": { 1308 | "version": "7.1.0", 1309 | "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", 1310 | "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", 1311 | "license": "MIT", 1312 | "engines": { 1313 | "node": ">=18" 1314 | }, 1315 | "funding": { 1316 | "url": "https://github.com/sponsors/sindresorhus" 1317 | } 1318 | }, 1319 | "node_modules/safe-buffer": { 1320 | "version": "5.2.1", 1321 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1322 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1323 | "funding": [ 1324 | { 1325 | "type": "github", 1326 | "url": "https://github.com/sponsors/feross" 1327 | }, 1328 | { 1329 | "type": "patreon", 1330 | "url": "https://www.patreon.com/feross" 1331 | }, 1332 | { 1333 | "type": "consulting", 1334 | "url": "https://feross.org/support" 1335 | } 1336 | ], 1337 | "license": "MIT" 1338 | }, 1339 | "node_modules/safer-buffer": { 1340 | "version": "2.1.2", 1341 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1342 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1343 | "license": "MIT" 1344 | }, 1345 | "node_modules/semver": { 1346 | "version": "7.7.2", 1347 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", 1348 | "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", 1349 | "license": "ISC", 1350 | "bin": { 1351 | "semver": "bin/semver.js" 1352 | }, 1353 | "engines": { 1354 | "node": ">=10" 1355 | } 1356 | }, 1357 | "node_modules/seq-queue": { 1358 | "version": "0.0.5", 1359 | "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", 1360 | "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" 1361 | }, 1362 | "node_modules/simple-concat": { 1363 | "version": "1.0.1", 1364 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 1365 | "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 1366 | "funding": [ 1367 | { 1368 | "type": "github", 1369 | "url": "https://github.com/sponsors/feross" 1370 | }, 1371 | { 1372 | "type": "patreon", 1373 | "url": "https://www.patreon.com/feross" 1374 | }, 1375 | { 1376 | "type": "consulting", 1377 | "url": "https://feross.org/support" 1378 | } 1379 | ], 1380 | "license": "MIT" 1381 | }, 1382 | "node_modules/simple-get": { 1383 | "version": "4.0.1", 1384 | "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", 1385 | "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", 1386 | "funding": [ 1387 | { 1388 | "type": "github", 1389 | "url": "https://github.com/sponsors/feross" 1390 | }, 1391 | { 1392 | "type": "patreon", 1393 | "url": "https://www.patreon.com/feross" 1394 | }, 1395 | { 1396 | "type": "consulting", 1397 | "url": "https://feross.org/support" 1398 | } 1399 | ], 1400 | "license": "MIT", 1401 | "dependencies": { 1402 | "decompress-response": "^6.0.0", 1403 | "once": "^1.3.1", 1404 | "simple-concat": "^1.0.0" 1405 | } 1406 | }, 1407 | "node_modules/split2": { 1408 | "version": "4.2.0", 1409 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 1410 | "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 1411 | "license": "ISC", 1412 | "engines": { 1413 | "node": ">= 10.x" 1414 | } 1415 | }, 1416 | "node_modules/sprintf-js": { 1417 | "version": "1.1.3", 1418 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", 1419 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", 1420 | "license": "BSD-3-Clause" 1421 | }, 1422 | "node_modules/sql-formatter": { 1423 | "version": "15.6.10", 1424 | "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.10.tgz", 1425 | "integrity": "sha512-0bJOPQrRO/JkjQhiThVayq0hOKnI1tHI+2OTkmT7TGtc6kqS+V7kveeMzRW+RNQGxofmTmet9ILvztyuxv0cJQ==", 1426 | "license": "MIT", 1427 | "dependencies": { 1428 | "argparse": "^2.0.1", 1429 | "nearley": "^2.20.1" 1430 | }, 1431 | "bin": { 1432 | "sql-formatter": "bin/sql-formatter-cli.cjs" 1433 | } 1434 | }, 1435 | "node_modules/sqlstring": { 1436 | "version": "2.3.3", 1437 | "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", 1438 | "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", 1439 | "license": "MIT", 1440 | "engines": { 1441 | "node": ">= 0.6" 1442 | } 1443 | }, 1444 | "node_modules/string_decoder": { 1445 | "version": "1.3.0", 1446 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1447 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1448 | "license": "MIT", 1449 | "dependencies": { 1450 | "safe-buffer": "~5.2.0" 1451 | } 1452 | }, 1453 | "node_modules/strip-json-comments": { 1454 | "version": "2.0.1", 1455 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1456 | "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 1457 | "license": "MIT", 1458 | "engines": { 1459 | "node": ">=0.10.0" 1460 | } 1461 | }, 1462 | "node_modules/tar-fs": { 1463 | "version": "2.1.4", 1464 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", 1465 | "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", 1466 | "license": "MIT", 1467 | "dependencies": { 1468 | "chownr": "^1.1.1", 1469 | "mkdirp-classic": "^0.5.2", 1470 | "pump": "^3.0.0", 1471 | "tar-stream": "^2.1.4" 1472 | } 1473 | }, 1474 | "node_modules/tar-stream": { 1475 | "version": "2.2.0", 1476 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 1477 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 1478 | "license": "MIT", 1479 | "dependencies": { 1480 | "bl": "^4.0.3", 1481 | "end-of-stream": "^1.4.1", 1482 | "fs-constants": "^1.0.0", 1483 | "inherits": "^2.0.3", 1484 | "readable-stream": "^3.1.1" 1485 | }, 1486 | "engines": { 1487 | "node": ">=6" 1488 | } 1489 | }, 1490 | "node_modules/tar-stream/node_modules/bl": { 1491 | "version": "4.1.0", 1492 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 1493 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 1494 | "license": "MIT", 1495 | "dependencies": { 1496 | "buffer": "^5.5.0", 1497 | "inherits": "^2.0.4", 1498 | "readable-stream": "^3.4.0" 1499 | } 1500 | }, 1501 | "node_modules/tar-stream/node_modules/buffer": { 1502 | "version": "5.7.1", 1503 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 1504 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 1505 | "funding": [ 1506 | { 1507 | "type": "github", 1508 | "url": "https://github.com/sponsors/feross" 1509 | }, 1510 | { 1511 | "type": "patreon", 1512 | "url": "https://www.patreon.com/feross" 1513 | }, 1514 | { 1515 | "type": "consulting", 1516 | "url": "https://feross.org/support" 1517 | } 1518 | ], 1519 | "license": "MIT", 1520 | "dependencies": { 1521 | "base64-js": "^1.3.1", 1522 | "ieee754": "^1.1.13" 1523 | } 1524 | }, 1525 | "node_modules/tar-stream/node_modules/readable-stream": { 1526 | "version": "3.6.2", 1527 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1528 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1529 | "license": "MIT", 1530 | "dependencies": { 1531 | "inherits": "^2.0.3", 1532 | "string_decoder": "^1.1.1", 1533 | "util-deprecate": "^1.0.1" 1534 | }, 1535 | "engines": { 1536 | "node": ">= 6" 1537 | } 1538 | }, 1539 | "node_modules/tarn": { 1540 | "version": "3.0.2", 1541 | "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", 1542 | "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", 1543 | "license": "MIT", 1544 | "engines": { 1545 | "node": ">=8.0.0" 1546 | } 1547 | }, 1548 | "node_modules/tedious": { 1549 | "version": "19.0.0", 1550 | "resolved": "https://registry.npmjs.org/tedious/-/tedious-19.0.0.tgz", 1551 | "integrity": "sha512-nmxNBAT72mMVCIYp0Ts0Zzd5+LBQjoXlqigCrIjSo2OERSi04vr3EHq3qJxv/zgrSkg7si03SoIIfekTAadA7w==", 1552 | "license": "MIT", 1553 | "dependencies": { 1554 | "@azure/core-auth": "^1.7.2", 1555 | "@azure/identity": "^4.2.1", 1556 | "@azure/keyvault-keys": "^4.4.0", 1557 | "@js-joda/core": "^5.6.1", 1558 | "@types/node": ">=18", 1559 | "bl": "^6.0.11", 1560 | "iconv-lite": "^0.6.3", 1561 | "js-md4": "^0.3.2", 1562 | "native-duplexpair": "^1.0.0", 1563 | "sprintf-js": "^1.1.3" 1564 | }, 1565 | "engines": { 1566 | "node": ">=18.17" 1567 | } 1568 | }, 1569 | "node_modules/tslib": { 1570 | "version": "2.8.1", 1571 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 1572 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 1573 | "license": "0BSD" 1574 | }, 1575 | "node_modules/tunnel-agent": { 1576 | "version": "0.6.0", 1577 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1578 | "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", 1579 | "license": "Apache-2.0", 1580 | "dependencies": { 1581 | "safe-buffer": "^5.0.1" 1582 | }, 1583 | "engines": { 1584 | "node": "*" 1585 | } 1586 | }, 1587 | "node_modules/undici-types": { 1588 | "version": "7.14.0", 1589 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", 1590 | "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", 1591 | "license": "MIT" 1592 | }, 1593 | "node_modules/util-deprecate": { 1594 | "version": "1.0.2", 1595 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1596 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1597 | "license": "MIT" 1598 | }, 1599 | "node_modules/uuid": { 1600 | "version": "8.3.2", 1601 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 1602 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 1603 | "license": "MIT", 1604 | "bin": { 1605 | "uuid": "dist/bin/uuid" 1606 | } 1607 | }, 1608 | "node_modules/wrappy": { 1609 | "version": "1.0.2", 1610 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1611 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1612 | "license": "ISC" 1613 | }, 1614 | "node_modules/wsl-utils": { 1615 | "version": "0.1.0", 1616 | "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", 1617 | "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", 1618 | "license": "MIT", 1619 | "dependencies": { 1620 | "is-wsl": "^3.1.0" 1621 | }, 1622 | "engines": { 1623 | "node": ">=18" 1624 | }, 1625 | "funding": { 1626 | "url": "https://github.com/sponsors/sindresorhus" 1627 | } 1628 | }, 1629 | "node_modules/xtend": { 1630 | "version": "4.0.2", 1631 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1632 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1633 | "license": "MIT", 1634 | "engines": { 1635 | "node": ">=0.4" 1636 | } 1637 | } 1638 | } 1639 | } 1640 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "./server/main.js", 3 | "scripts": { 4 | "start": "node ./server/main.js" 5 | }, 6 | "type": "module", 7 | "dependencies": { 8 | "better-sqlite3": "^12.4.1", 9 | "mssql": "^12.0.0", 10 | "mysql2": "^3.15.2", 11 | "pg": "^8.16.3", 12 | "sql-formatter": "^15.6.10" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/consumer.js: -------------------------------------------------------------------------------- 1 | import { driver } from "./driver.js"; 2 | 3 | export class Consumer { 4 | static async createConnection(params) { 5 | const { id, dbType, connStr } = params; 6 | return await driver.createConnection(id, dbType, connStr); 7 | } 8 | 9 | static async getTableList(params) { 10 | const { id } = params; 11 | return await driver.getTableList(id); 12 | } 13 | 14 | static async getViewList(params) { 15 | const { id } = params; 16 | return await driver.getViewList(id); 17 | } 18 | 19 | static async getStoreProcedureList(params) { 20 | const { id } = params; 21 | return await driver.getStoreProcedureList(id); 22 | } 23 | 24 | static async getFunctionList(params) { 25 | const { id } = params; 26 | return await driver.getFunctionList(id); 27 | } 28 | 29 | static async getView(params) { 30 | const { id, view_name } = params; 31 | return await driver.getView(id, view_name); 32 | } 33 | 34 | static async getStoreProcedure(params) { 35 | const { id, procedure_name } = params; 36 | return await driver.getStoreProcedure(id, procedure_name); 37 | } 38 | 39 | static async getFunction(params) { 40 | const { id, function_name } = params; 41 | return await driver.getFunction(id, function_name); 42 | } 43 | 44 | static async query(params) { 45 | const { id, sql } = params; 46 | return await driver.query(id, sql); 47 | } 48 | 49 | static async getTable(params) { 50 | const { id, table_name } = params; 51 | return await driver.getTable(id, table_name); 52 | } 53 | 54 | static async getTrigger(params) { 55 | const { id, trig_name } = params; 56 | return await driver.getTrigger(id, trig_name); 57 | } 58 | 59 | static async getTriggerList(params) { 60 | const { id, table_name } = params; 61 | return await driver.getTriggerList(id, table_name); 62 | } 63 | 64 | static async generateSelectSQL(params) { 65 | const { id, table_name } = params; 66 | return await driver.generateSelectSQL(id, table_name); 67 | } 68 | 69 | static async generateInsertSQL(params) { 70 | const { id, table_name } = params; 71 | return await driver.generateInsertSQL(id, table_name); 72 | } 73 | 74 | static async generateUpdateSQL(params) { 75 | const { id, table_name } = params; 76 | return await driver.generateUpdateSQL(id, table_name); 77 | } 78 | 79 | static async format(params) { 80 | const { id, sql } = params; 81 | return await driver.format(id, sql); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /server/db/mssql.js: -------------------------------------------------------------------------------- 1 | import sql from "mssql"; 2 | import { format } from "sql-formatter"; 3 | 4 | export class MsSql { 5 | #pool; 6 | 7 | async #init(config) { 8 | this.#pool = await new sql.ConnectionPool(config).connect(); 9 | } 10 | 11 | static async createConnection(conn_str) { 12 | const config = sql.ConnectionPool.parseConnectionString(conn_str); 13 | const instance = new MsSql(); 14 | await instance.#init(config); 15 | return instance; 16 | } 17 | 18 | format(sql) { 19 | return format(sql, { 20 | language: "tsql", 21 | }); 22 | } 23 | 24 | async query(sql) { 25 | const start = Date.now(); 26 | const result = await this.#pool.request().query(sql); 27 | const end = Date.now(); 28 | 29 | return { 30 | duration: `${end - start}ms`, 31 | total: result.rowsAffected[0], 32 | rows: result.recordset ?? [], 33 | }; 34 | } 35 | 36 | async getTableList() { 37 | const sql = ` 38 | SELECT TABLE_NAME as table_name 39 | FROM INFORMATION_SCHEMA.TABLES 40 | WHERE TABLE_TYPE = 'BASE TABLE' 41 | ORDER BY TABLE_NAME 42 | `; 43 | return await this.query(sql); 44 | } 45 | 46 | async getViewList() { 47 | const sql = ` 48 | SELECT TABLE_NAME as view_name 49 | FROM INFORMATION_SCHEMA.TABLES 50 | WHERE TABLE_TYPE = 'VIEW' 51 | ORDER BY TABLE_NAME 52 | `; 53 | return await this.query(sql); 54 | } 55 | 56 | async getStoreProcedureList() { 57 | const sql = ` 58 | SELECT 59 | ROUTINE_SCHEMA as schema_name, 60 | ROUTINE_NAME as procedure_name 61 | FROM INFORMATION_SCHEMA.ROUTINES 62 | WHERE ROUTINE_TYPE = 'PROCEDURE' 63 | ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME; 64 | `; 65 | return await this.query(sql); 66 | } 67 | 68 | async getFunctionList() { 69 | const sql = ` 70 | SELECT 71 | ROUTINE_SCHEMA as schema_name, 72 | ROUTINE_NAME as function_name 73 | FROM INFORMATION_SCHEMA.ROUTINES 74 | WHERE ROUTINE_TYPE = 'FUNCTION' 75 | ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME; 76 | `; 77 | return await this.query(sql); 78 | } 79 | 80 | async getView(view_name) { 81 | const sql = ` 82 | SELECT VIEW_DEFINITION as 'definition' 83 | FROM INFORMATION_SCHEMA.VIEWS 84 | WHERE TABLE_NAME = '${view_name}' 85 | `; 86 | return await this.query(sql); 87 | } 88 | 89 | async getStoreProcedure(procedure_name) { 90 | const sql = ` 91 | SELECT 92 | m.definition AS definition 93 | FROM sys.procedures p 94 | INNER JOIN sys.sql_modules m ON p.object_id = m.object_id 95 | WHERE p.name = '${procedure_name}'; 96 | `; 97 | return await this.query(sql); 98 | } 99 | 100 | async getFunction(function_name) { 101 | const sql = ` 102 | SELECT 103 | m.definition 104 | FROM sys.objects o 105 | JOIN sys.sql_modules m ON o.object_id = m.object_id 106 | JOIN sys.schemas s ON o.schema_id = s.schema_id 107 | WHERE o.type IN ('FN', 'IF', 'TF') 108 | AND o.name = '${function_name}'; 109 | `; 110 | return await this.query(sql); 111 | } 112 | 113 | async getTable(table_name) { 114 | const sql = ` 115 | SELECT 116 | c.column_id AS column_id, 117 | c.name AS column_name, 118 | t.name AS data_type, 119 | c.max_length AS max_length, 120 | c.is_nullable AS is_nullable, 121 | dc.definition AS default_value, 122 | MAX(CAST(CASE WHEN i.is_primary_key = 1 THEN 1 ELSE 0 END AS int)) AS is_pk, 123 | MAX(CAST(CASE WHEN i.is_unique = 1 THEN 1 ELSE 0 END AS int)) AS is_unique 124 | FROM sys.columns c 125 | LEFT JOIN sys.types t 126 | ON t.system_type_id = c.system_type_id 127 | AND t.user_type_id = t.system_type_id 128 | LEFT JOIN sys.default_constraints dc 129 | ON dc.object_id = c.default_object_id 130 | LEFT JOIN sys.index_columns ic 131 | ON ic.object_id = c.object_id 132 | AND c.column_id = ic.column_id 133 | LEFT JOIN sys.indexes i 134 | ON i.object_id = c.object_id 135 | AND i.index_id = ic.index_id 136 | WHERE OBJECT_NAME(c.object_id) = '${table_name}' 137 | GROUP BY c.column_id, c.name, t.name, c.max_length, c.is_nullable, dc.definition 138 | ORDER BY c.column_id; 139 | `; 140 | const result = await this.query(sql); 141 | result.rows = result.rows.map((item) => { 142 | return { 143 | column_name: item.column_name, 144 | data_type: item.data_type, 145 | max_length: item.max_length, 146 | is_nullable: item.is_nullable, 147 | default_value: item.default_value, 148 | is_pk: item.is_pk === 1 ? true : false, 149 | is_unique: item.is_unique === 1 ? true : false, 150 | }; 151 | }); 152 | return result; 153 | } 154 | 155 | async getTrigger(trig_name) { 156 | const sql = ` 157 | SELECT m.definition 158 | FROM sys.sql_modules m 159 | JOIN sys.triggers t ON m.object_id = t.object_id 160 | WHERE t.name = '${trig_name}'; 161 | `; 162 | return await this.query(sql); 163 | } 164 | 165 | async getTriggerList(table_name) { 166 | const sql = ` 167 | SELECT t.name AS trigger_name 168 | FROM sys.triggers t 169 | WHERE t.parent_id = OBJECT_ID('${table_name}'); 170 | `; 171 | return await this.query(sql); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /server/db/mysql.js: -------------------------------------------------------------------------------- 1 | import mysql from "mysql2/promise"; 2 | import { format } from "sql-formatter"; 3 | 4 | export class MySql { 5 | #pool; 6 | 7 | async #init(config) { 8 | this.#pool = mysql.createPool(config); 9 | } 10 | 11 | static async createConnection(conn_str) { 12 | const instance = new MySql(); 13 | await instance.#init(conn_str); 14 | return instance; 15 | } 16 | 17 | async query(sql) { 18 | const start = Date.now(); 19 | const [result, _] = await this.#pool.execute(sql); 20 | const end = Date.now(); 21 | 22 | let total, rows; 23 | if (Array.isArray(result)) { 24 | rows = result; 25 | total = result.length; 26 | } else { 27 | rows = []; 28 | total = result.affectedRows; 29 | } 30 | return { 31 | duration: `${end - start}ms`, 32 | total, 33 | rows, 34 | }; 35 | } 36 | 37 | format(sql) { 38 | return format(sql, { 39 | language: "mysql", 40 | }); 41 | } 42 | 43 | async getTableList() { 44 | const sql = ` 45 | SELECT TABLE_NAME as table_name 46 | FROM information_schema.tables 47 | WHERE table_schema = DATABASE() 48 | AND table_type = 'BASE TABLE' 49 | ORDER BY TABLE_NAME 50 | `; 51 | return await this.query(sql); 52 | } 53 | 54 | async getViewList() { 55 | const sql = ` 56 | SELECT TABLE_NAME as view_name 57 | FROM information_schema.tables 58 | WHERE table_schema = DATABASE() 59 | AND table_type = 'VIEW' 60 | ORDER BY TABLE_NAME 61 | `; 62 | return await this.query(sql); 63 | } 64 | 65 | async getStoreProcedureList() { 66 | const sql = ` 67 | SELECT 68 | ROUTINE_SCHEMA as schema_name, 69 | ROUTINE_NAME as procedure_name 70 | FROM INFORMATION_SCHEMA.ROUTINES 71 | WHERE ROUTINE_TYPE = 'PROCEDURE' 72 | AND ROUTINE_SCHEMA = DATABASE() 73 | ORDER BY ROUTINE_NAME; 74 | `; 75 | return await this.query(sql); 76 | } 77 | 78 | async getFunctionList() { 79 | const sql = ` 80 | SELECT 81 | ROUTINE_SCHEMA as schema_name, 82 | ROUTINE_NAME as function_name 83 | FROM INFORMATION_SCHEMA.ROUTINES 84 | WHERE ROUTINE_TYPE = 'FUNCTION' 85 | AND ROUTINE_SCHEMA = DATABASE() 86 | ORDER BY ROUTINE_NAME; 87 | `; 88 | return await this.query(sql); 89 | } 90 | 91 | async getView(view_name) { 92 | const sql = ` 93 | SELECT 94 | VIEW_DEFINITION as 'definition' 95 | FROM INFORMATION_SCHEMA.VIEWS 96 | WHERE TABLE_NAME = '${view_name}' 97 | `; 98 | return await this.query(sql); 99 | } 100 | 101 | async getStoreProcedure(procedure_name) { 102 | const sql = ` 103 | SELECT 104 | ROUTINE_DEFINITION as 'definition' 105 | FROM information_schema.ROUTINES 106 | WHERE ROUTINE_NAME = '${procedure_name}' 107 | AND ROUTINE_TYPE = 'PROCEDURE'; 108 | `; 109 | return await this.query(sql); 110 | } 111 | 112 | async getFunction(function_name) { 113 | const sql = ` 114 | SELECT 115 | ROUTINE_DEFINITION as 'definition' 116 | FROM information_schema.ROUTINES 117 | WHERE ROUTINE_NAME = '${function_name}' 118 | AND ROUTINE_TYPE = 'FUNCTION'; 119 | `; 120 | return await this.query(sql); 121 | } 122 | 123 | async getTable(table_name) { 124 | const sql = ` 125 | SELECT 126 | ORDINAL_POSITION AS column_id, 127 | COLUMN_NAME AS column_name, 128 | DATA_TYPE AS data_type, 129 | CHARACTER_MAXIMUM_LENGTH AS max_length, 130 | (IS_NULLABLE = 'YES') AS is_nullable, 131 | COLUMN_DEFAULT AS default_value, 132 | (COLUMN_KEY = 'PRI') AS is_pk, 133 | (COLUMN_KEY = 'UNI') AS is_unique 134 | FROM INFORMATION_SCHEMA.COLUMNS 135 | WHERE TABLE_SCHEMA = DATABASE() 136 | AND TABLE_NAME = '${table_name}' 137 | ORDER BY ORDINAL_POSITION; 138 | `; 139 | const result = await this.query(sql); 140 | result.rows = result.rows.map((item) => { 141 | return { 142 | column_name: item.column_name, 143 | data_type: item.data_type, 144 | max_length: item.max_length, 145 | is_nullable: item.is_nullable == "1" ? true : false, 146 | default_value: item.default_value, 147 | is_pk: item.is_pk === 1 ? true : false, 148 | is_unique: item.is_unique === 1 ? true : false, 149 | }; 150 | }); 151 | return result; 152 | } 153 | 154 | async getTriggerList(table_name) { 155 | const sql = ` 156 | SELECT TRIGGER_NAME as trigger_name 157 | FROM information_schema.triggers 158 | WHERE EVENT_OBJECT_TABLE = '${table_name}' 159 | AND TRIGGER_SCHEMA = DATABASE(); 160 | `; 161 | return await this.query(sql); 162 | } 163 | 164 | async getTrigger(trig_name) { 165 | const sql = ` 166 | SELECT ACTION_STATEMENT AS definition 167 | FROM information_schema.triggers 168 | WHERE TRIGGER_NAME = '${trig_name}' 169 | AND TRIGGER_SCHEMA = DATABASE(); 170 | `; 171 | return await this.query(sql); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /server/db/postgres.js: -------------------------------------------------------------------------------- 1 | import pkg from "pg"; 2 | import { format } from "sql-formatter"; 3 | 4 | const { Pool } = pkg; 5 | 6 | export class Postgres { 7 | #pool; 8 | 9 | async #init(config) { 10 | this.#pool = new Pool(config); 11 | } 12 | 13 | static async createConnection(conn_str) { 14 | const instance = new Postgres(); 15 | await instance.#init({ connectionString: conn_str }); 16 | return instance; 17 | } 18 | 19 | async query(sql) { 20 | const client = await this.#pool.connect(); 21 | try { 22 | const start = Date.now(); 23 | const result = await client.query(sql); 24 | const end = Date.now(); 25 | 26 | return { 27 | duration: `${end - start}ms`, 28 | total: result.rowCount, 29 | rows: result.rows, 30 | }; 31 | } finally { 32 | client.release(); 33 | } 34 | } 35 | 36 | format(sql) { 37 | return format(sql, { 38 | language: "postgresql", 39 | }); 40 | } 41 | 42 | async getTableList() { 43 | const sql = ` 44 | SELECT table_name 45 | FROM information_schema.tables 46 | WHERE table_schema = 'public' 47 | AND table_type = 'BASE TABLE' 48 | ORDER BY table_name 49 | `; 50 | return await this.query(sql); 51 | } 52 | 53 | async getViewList() { 54 | const sql = ` 55 | SELECT table_name as view_name 56 | FROM information_schema.tables 57 | WHERE table_schema = 'public' 58 | AND table_type = 'VIEW' 59 | ORDER BY table_name; 60 | `; 61 | return await this.query(sql); 62 | } 63 | 64 | async getStoreProcedureList() { 65 | const sql = ` 66 | SELECT 67 | routine_schema AS schema_name, 68 | routine_name AS procedure_name 69 | FROM information_schema.routines 70 | WHERE routine_type = 'PROCEDURE' 71 | AND routine_schema NOT IN ('pg_catalog', 'information_schema') 72 | ORDER BY routine_schema, routine_name; 73 | `; 74 | return await this.query(sql); 75 | } 76 | 77 | async getFunctionList() { 78 | const sql = ` 79 | SELECT 80 | routine_schema AS schema_name, 81 | routine_name AS function_name 82 | FROM information_schema.routines 83 | WHERE routine_type = 'FUNCTION' 84 | AND routine_schema NOT IN ('pg_catalog', 'information_schema') 85 | ORDER BY routine_schema, routine_name; 86 | `; 87 | return await this.query(sql); 88 | } 89 | 90 | async getView(view_name) { 91 | const sql = ` 92 | SELECT definition as 'definition' 93 | FROM pg_views 94 | WHERE viewname = '${view_name}' 95 | `; 96 | return await this.query(sql); 97 | } 98 | 99 | async getStoreProcedure(procedure_name) { 100 | const sql = ` 101 | SELECT 102 | pg_get_functiondef(p.oid) AS definition 103 | FROM pg_proc p 104 | JOIN pg_namespace n ON n.oid = p.pronamespace 105 | WHERE p.prokind = 'p' 106 | AND p.proname = '${procedure_name}'; 107 | `; 108 | return await this.query(sql); 109 | } 110 | 111 | async getFunction(function_name) { 112 | const sql = ` 113 | SELECT 114 | pg_get_functiondef(p.oid) AS definition 115 | FROM pg_proc p 116 | JOIN pg_namespace n ON n.oid = p.pronamespace 117 | WHERE p.prokind = 'f' 118 | AND p.proname = '${function_name}'; 119 | `; 120 | return await this.query(sql); 121 | } 122 | 123 | async getTable(table_name) { 124 | const sql = ` 125 | SELECT 126 | a.attnum AS column_id, 127 | a.attname AS column_name, 128 | format_type(a.atttypid, a.atttypmod) AS data_type, 129 | col.character_maximum_length AS max_length, 130 | NOT a.attnotnull AS is_nullable, 131 | pg_get_expr(ad.adbin, ad.adrelid) AS default_value, 132 | CASE WHEN ct.contype = 'p' THEN 1 ELSE 0 END AS is_pk, 133 | CASE WHEN ct.contype = 'u' THEN 1 ELSE 0 END AS is_unique 134 | FROM pg_attribute a 135 | JOIN pg_class c ON a.attrelid = c.oid 136 | JOIN pg_namespace n ON n.oid = c.relnamespace 137 | LEFT JOIN information_schema.columns col 138 | ON col.table_schema = n.nspname AND col.table_name = c.relname AND col.column_name = a.attname 139 | LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum 140 | LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid AND a.attnum = ANY(ct.conkey) 141 | WHERE c.relname = '${table_name}' 142 | AND a.attnum > 0 143 | AND NOT a.attisdropped 144 | GROUP BY a.attnum, a.attname, format_type(a.atttypid, a.atttypmod), 145 | col.character_maximum_length, a.attnotnull, ad.adbin, ad.adrelid, ct.contype 146 | ORDER BY a.attnum; 147 | `; 148 | const result = await this.query(sql); 149 | result.rows = result.rows.map((item) => { 150 | return { 151 | column_name: item.column_name, 152 | data_type: item.data_type, 153 | max_length: item.max_length, 154 | is_nullable: item.is_nullable, 155 | default_value: item.default_value, 156 | is_pk: item.is_pk === 1 ? true : false, 157 | is_unique: item.is_unique === 1 ? true : false, 158 | }; 159 | }); 160 | return result; 161 | } 162 | 163 | async getTriggerList(table_name) { 164 | const sql = ` 165 | SELECT tg.tgname AS trigger_name 166 | FROM pg_trigger tg 167 | JOIN pg_class tbl ON tg.tgrelid = tbl.oid 168 | JOIN pg_namespace ns ON tbl.relnamespace = ns.oid 169 | WHERE tbl.relname = '${table_name}' 170 | AND ns.nspname = 'public' 171 | AND NOT tg.tgisinternal; 172 | `; 173 | return await this.query(sql); 174 | } 175 | 176 | async getTrigger(trig_name) { 177 | const sql = ` 178 | SELECT pg_get_triggerdef(t.oid, true) AS definition 179 | FROM pg_trigger t 180 | JOIN pg_class c ON t.tgrelid = c.oid 181 | JOIN pg_namespace n ON c.relnamespace = n.oid 182 | WHERE t.tgname = '${trig_name}' 183 | AND NOT t.tgisinternal; 184 | `; 185 | return await this.query(sql); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /server/db/sqlite.js: -------------------------------------------------------------------------------- 1 | import Database from "better-sqlite3"; 2 | import { format } from "sql-formatter"; 3 | 4 | export class Sqlite { 5 | #db; 6 | 7 | constructor(file_path) { 8 | this.#db = new Database(file_path); 9 | } 10 | 11 | static async createConnection(file_path) { 12 | return new Sqlite(file_path); 13 | } 14 | 15 | query(sql) { 16 | const stmt = this.#db.prepare(sql); 17 | 18 | let total, rows; 19 | const start = Date.now(); 20 | try { 21 | rows = stmt.all(); 22 | total = rows.length; 23 | } catch (err) { 24 | const info = stmt.run(); 25 | rows = []; 26 | total = info.changes; 27 | } 28 | const end = Date.now(); 29 | 30 | return { 31 | duration: `${end - start}ms`, 32 | total, 33 | rows, 34 | }; 35 | } 36 | 37 | format(sql) { 38 | return format(sql, { 39 | language: "sqlite", 40 | }); 41 | } 42 | 43 | getTableList() { 44 | const sql = ` 45 | SELECT name as table_name 46 | FROM sqlite_master 47 | WHERE type='table' 48 | AND name NOT LIKE 'sqlite_%' 49 | ORDER BY name; 50 | `; 51 | return this.query(sql); 52 | } 53 | 54 | getViewList() { 55 | const sql = ` 56 | SELECT name as view_name 57 | FROM sqlite_master 58 | WHERE type='view' 59 | AND name NOT LIKE 'sqlite_%' 60 | ORDER BY name; 61 | `; 62 | return this.query(sql); 63 | } 64 | 65 | getStoreProcedureList() { 66 | return "Not Supported"; 67 | } 68 | 69 | getFunctionList() { 70 | return "Not Supported"; 71 | } 72 | 73 | getView(view_name) { 74 | const sql = ` 75 | SELECT sql as 'definition' 76 | FROM sqlite_master 77 | WHERE type = 'view' 78 | AND name = '${view_name}' 79 | `; 80 | return this.query(sql); 81 | } 82 | 83 | getStoreProcedure() { 84 | return "Not Supported"; 85 | } 86 | 87 | getFunction() { 88 | return "Not Supported"; 89 | } 90 | 91 | getTable(table_name) { 92 | const indexes = this.query(`PRAGMA index_list('${table_name}');`); 93 | 94 | const indexMap = {}; 95 | for (const idx of indexes.rows) { 96 | if (idx.unique) { 97 | const cols = this.query(`PRAGMA index_info('${idx.name}')`); 98 | for (const col of cols.rows) { 99 | indexMap[col.name] = idx.name; 100 | } 101 | } 102 | } 103 | 104 | const result = this.query(`PRAGMA table_info('${table_name}');`); 105 | result.rows = result.rows.map((item) => { 106 | return { 107 | column_name: item.name, 108 | data_type: item.type, 109 | max_length: null, 110 | is_nullable: item.notnull === 1 ? true : false, 111 | default_value: item.dflt_value, 112 | is_pk: item.pk === 1 ? true : false, 113 | is_unique: indexMap[item.column_name] ? true : false, 114 | }; 115 | }); 116 | return result; 117 | } 118 | 119 | getTriggerList(table_name) { 120 | const sql = ` 121 | SELECT name AS trigger_name 122 | FROM sqlite_master 123 | WHERE type = 'trigger' 124 | AND tbl_name = '${table_name}'; 125 | `; 126 | return this.query(sql); 127 | } 128 | 129 | getTrigger(trig_name) { 130 | const sql = ` 131 | SELECT sql AS definition 132 | FROM sqlite_master 133 | WHERE type = 'trigger' 134 | AND name = '${trig_name}'; 135 | `; 136 | return this.query(sql); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /server/driver.js: -------------------------------------------------------------------------------- 1 | import { MsSql } from "./db/mssql.js"; 2 | import { MySql } from "./db/mysql.js"; 3 | import { Postgres } from "./db/postgres.js"; 4 | import { Sqlite } from "./db/sqlite.js"; 5 | 6 | const DB_TYPE = { 7 | MSSQL: "mssql", 8 | SQLITE: "sqlite3", 9 | POSTGRES: "postgresql", 10 | MYSQL: "mysql", 11 | }; 12 | 13 | const DB_MAP = { 14 | [DB_TYPE.MSSQL]: MsSql, 15 | [DB_TYPE.SQLITE]: Sqlite, 16 | [DB_TYPE.POSTGRES]: Postgres, 17 | [DB_TYPE.MYSQL]: MySql, 18 | }; 19 | 20 | class Driver { 21 | #connections = new Map(); 22 | 23 | async createConnection(id, db_type, conn_str) { 24 | if (this.#connections.has(id)) { 25 | return "connected"; 26 | } 27 | 28 | const db = DB_MAP[db_type]; 29 | if (!db) { 30 | throw new Error(`${db_type} is not supported`); 31 | } 32 | 33 | const conn = await db.createConnection(conn_str); 34 | this.#connections.set(id, conn); 35 | return "connected"; 36 | } 37 | 38 | async query(id, sql) { 39 | const conn = this.#connections.get(id); 40 | return await conn.query(sql); 41 | } 42 | 43 | async getTableList(id) { 44 | const conn = this.#connections.get(id); 45 | return await conn.getTableList(); 46 | } 47 | 48 | async getViewList(id) { 49 | const conn = this.#connections.get(id); 50 | return await conn.getViewList(); 51 | } 52 | 53 | async getStoreProcedureList(id) { 54 | const conn = this.#connections.get(id); 55 | return await conn.getStoreProcedureList(); 56 | } 57 | 58 | async getFunctionList(id) { 59 | const conn = this.#connections.get(id); 60 | return await conn.getFunctionList(); 61 | } 62 | 63 | async getView(id, view_name) { 64 | const conn = this.#connections.get(id); 65 | return await conn.getView(view_name); 66 | } 67 | 68 | async getStoreProcedure(id, procedure_name) { 69 | const conn = this.#connections.get(id); 70 | return await conn.getStoreProcedure(procedure_name); 71 | } 72 | 73 | async getFunction(id, function_name) { 74 | const conn = this.#connections.get(id); 75 | return await conn.getFunction(function_name); 76 | } 77 | 78 | async getTable(id, table_name) { 79 | const conn = this.#connections.get(id); 80 | return await conn.getTable(table_name); 81 | } 82 | 83 | async getTrigger(id, trig_name) { 84 | const conn = this.#connections.get(id); 85 | return await conn.getTrigger(trig_name); 86 | } 87 | 88 | async getTriggerList(id, table_name) { 89 | const conn = this.#connections.get(id); 90 | return await conn.getTriggerList(table_name); 91 | } 92 | 93 | async generateSelectSQL(id, table_name) { 94 | const conn = this.#connections.get(id); 95 | const table = await conn.getTable(table_name); 96 | const columns = table.rows.map((col) => { 97 | return col.column_name; 98 | }); 99 | const pkey = table.rows 100 | .filter((col) => col.is_pk) 101 | .map((col) => { 102 | return `${col.column_name} = @${col.column_name}`; 103 | }); 104 | 105 | const sql = `SELECT ${columns.join(",")} FROM ${table_name} WHERE ${pkey.join(" AND ")}`; 106 | return conn.format(sql); 107 | } 108 | 109 | async generateUpdateSQL(id, table_name) { 110 | const conn = this.#connections.get(id); 111 | const table = await conn.getTable(table_name); 112 | const columns = table.rows 113 | .filter((col) => !col.is_pk) 114 | .map((col) => { 115 | return `${col.column_name} = @${col.column_name}`; 116 | }); 117 | const pkey = table.rows 118 | .filter((col) => col.is_pk) 119 | .map((col) => { 120 | return `${col.column_name} = @${col.column_name}`; 121 | }); 122 | 123 | const sql = `UPDATE ${table_name} SET ${columns.join(",")} WHERE ${pkey.join(" AND ")}`; 124 | return conn.format(sql); 125 | } 126 | 127 | async generateInsertSQL(id, table_name) { 128 | const conn = this.#connections.get(id); 129 | const table = await conn.getTable(table_name); 130 | const columns = table.rows.map((col) => { 131 | return col.column_name; 132 | }); 133 | const values = table.rows.map((col) => { 134 | return `@${col.column_name}`; 135 | }); 136 | 137 | const sql = `INSERT INTO(${columns.join(",")}) VALUES (${values.join(",")})`; 138 | return conn.format(sql); 139 | } 140 | 141 | format(id, sql) { 142 | const conn = this.#connections.get(id); 143 | return conn.format(sql); 144 | } 145 | } 146 | 147 | export const driver = new Driver(); 148 | -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | import readline from "readline"; 2 | import { RPC } from "./rpc.js"; 3 | 4 | const rl = readline.createInterface({ 5 | input: process.stdin, 6 | output: process.stdout, 7 | terminal: false, 8 | }); 9 | 10 | rl.on("line", async (line) => { 11 | try { 12 | const req = RPC.parseData(line); 13 | RPC.validRequest(req); 14 | const res = await RPC.exec(req); 15 | if (res) { 16 | process.stdout.write(JSON.stringify(res) + "\n"); 17 | } 18 | } catch (err) { 19 | process.stderr.write(JSON.stringify(err) + "\n"); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /server/rpc.js: -------------------------------------------------------------------------------- 1 | import { Consumer } from "./consumer.js"; 2 | 3 | const METHODS = { 4 | CREATE_CONNECTION: "create_connection", 5 | GET_TABLE_LIST: "get_table_list", 6 | GET_VIEW_LIST: "get_view_list", 7 | GET_STORE_PROCEDURE_LIST: "get_store_procedure_list", 8 | GET_FUNCTION_LIST: "get_function_list", 9 | GET_VIEW: "get_view", 10 | GET_STORE_PROCEDURE: "get_store_procedure", 11 | GET_FUNCTION: "get_function", 12 | GET_TABLE: "get_table", 13 | GET_TRIGGER: "get_trigger", 14 | GET_TRIGGER_LIST: "get_trigger_list", 15 | GENERATE_SELECT_SQL: "generate_select_sql", 16 | GENERATE_INSERT_SQL: "generate_insert_sql", 17 | GENERATE_UPDATE_SQL: "generate_update_sql", 18 | QUERY: "query", 19 | FORMAT: "format", 20 | }; 21 | 22 | const handlers = { 23 | [METHODS.CREATE_CONNECTION]: (params) => Consumer.createConnection(params), 24 | [METHODS.GET_TABLE_LIST]: (params) => Consumer.getTableList(params), 25 | [METHODS.GET_VIEW_LIST]: (params) => Consumer.getViewList(params), 26 | [METHODS.QUERY]: (params) => Consumer.query(params), 27 | [METHODS.GET_STORE_PROCEDURE_LIST]: (params) => 28 | Consumer.getStoreProcedureList(params), 29 | [METHODS.GET_FUNCTION_LIST]: (params) => Consumer.getFunctionList(params), 30 | [METHODS.GET_VIEW]: (params) => Consumer.getView(params), 31 | [METHODS.GET_STORE_PROCEDURE]: (params) => Consumer.getStoreProcedure(params), 32 | [METHODS.GET_FUNCTION]: (params) => Consumer.getFunction(params), 33 | [METHODS.GET_TABLE]: (params) => Consumer.getTable(params), 34 | [METHODS.GET_TRIGGER]: (params) => Consumer.getTrigger(params), 35 | [METHODS.GET_TRIGGER_LIST]: (params) => Consumer.getTriggerList(params), 36 | [METHODS.GENERATE_SELECT_SQL]: (params) => Consumer.generateSelectSQL(params), 37 | [METHODS.GENERATE_INSERT_SQL]: (params) => Consumer.generateInsertSQL(params), 38 | [METHODS.GENERATE_UPDATE_SQL]: (params) => Consumer.generateUpdateSQL(params), 39 | [METHODS.FORMAT]: (params) => Consumer.format(params), 40 | }; 41 | 42 | export class RPC { 43 | static parseData(data) { 44 | try { 45 | return JSON.parse(data); 46 | } catch (err) { 47 | throw this.parseError(err); 48 | } 49 | } 50 | 51 | static validRequest(decoded) { 52 | if (!decoded.jsonrpc) { 53 | throw this.invalidRequest("jsonrpc is required"); 54 | } 55 | 56 | if (decoded.jsonrpc != "2.0") { 57 | throw this.invalidRequest("jsonrpc 2.0 only supported"); 58 | } 59 | 60 | if (!decoded.method) { 61 | throw this.invalidRequest("method is required"); 62 | } 63 | 64 | if (typeof decoded.method !== "string") { 65 | throw this.invalidRequest("method must be a string"); 66 | } 67 | 68 | if (decoded.params !== undefined && typeof decoded.params !== "object") { 69 | throw this.invalidRequest( 70 | "params must be an array or object if provided", 71 | ); 72 | } 73 | 74 | if (decoded.id !== undefined) { 75 | const t = typeof decoded.id; 76 | if (!(t === "string" || t === "number" || decoded.id === null)) { 77 | throw this.invalidRequest( 78 | "id must be string, number, or null if provided", 79 | ); 80 | } 81 | } 82 | } 83 | 84 | static async exec(req) { 85 | const { id, method, params } = req; 86 | 87 | if (!handlers[method]) throw this.methodNotFound(id, `${method} not found`); 88 | 89 | try { 90 | const data = await handlers[method](params); 91 | if (data) { 92 | return this.ok(id, data); 93 | } 94 | } catch (err) { 95 | throw this.internalError(req.id, err.stack); 96 | } 97 | } 98 | 99 | static ok(id, result) { 100 | return { 101 | jsonrpc: "2.0", 102 | result: JSON.stringify(result, null, 2), 103 | id, 104 | }; 105 | } 106 | 107 | static methodNotFound(id, data) { 108 | return { 109 | jsonrpc: "2.0", 110 | id, 111 | error: { 112 | code: -32601, 113 | message: "Method not found", 114 | data, 115 | }, 116 | }; 117 | } 118 | 119 | static parseError(data) { 120 | return { 121 | jsonrpc: "2.0", 122 | error: { 123 | code: -32700, 124 | message: "Parse error", 125 | data, 126 | }, 127 | }; 128 | } 129 | 130 | static invalidRequest(data) { 131 | return { 132 | jsonrpc: "2.0", 133 | error: { 134 | code: -32600, 135 | message: "Invalid Request", 136 | data, 137 | }, 138 | }; 139 | } 140 | 141 | static internalError(id, data) { 142 | return { 143 | jsonrpc: "2.0", 144 | id, 145 | error: { 146 | code: -32603, 147 | message: "Internal error", 148 | data, 149 | }, 150 | }; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | line_endings = "Unix" 2 | indent_type = "Spaces" 3 | indent_width = 2 4 | quote_style = "AutoPreferDouble" 5 | call_parentheses = "Always" 6 | --------------------------------------------------------------------------------