@dataramen/cli 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/run.js +2 -169
- package/dist/README.md +67 -0
- package/dist/code/cli.js +2 -169
- package/dist/code/proxy.js +8 -8
- package/dist/package.json +20 -1
- package/package.json +27 -2
package/bin/run.js
CHANGED
|
@@ -1,169 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const path = require("node:path");
|
|
4
|
-
const yargs = require("yargs");
|
|
5
|
-
const yargsHelpers = require("yargs/helpers");
|
|
6
|
-
const os = require('node:os');
|
|
7
|
-
const fs = require("fs-extra");
|
|
8
|
-
|
|
9
|
-
const processName = "@dataramen/local-server";
|
|
10
|
-
|
|
11
|
-
const homeDir = os.homedir();
|
|
12
|
-
const filesPath = path.join(homeDir, ".dataramen", ".runtime", "server");
|
|
13
|
-
|
|
14
|
-
const scriptPkg = useJson(path.join(__dirname, "..", "package.json"));
|
|
15
|
-
const serverPkg = useJson(path.join(filesPath, "package.json"));
|
|
16
|
-
|
|
17
|
-
const protocol = "dataramen://";
|
|
18
|
-
|
|
19
|
-
yargs()
|
|
20
|
-
.command("start", 'Default command, start/restart the server', () => {
|
|
21
|
-
const hasPm2 = checkPm2();
|
|
22
|
-
if (!hasPm2) {
|
|
23
|
-
installPm2();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
start();
|
|
27
|
-
})
|
|
28
|
-
.command(["logs"], 'Listen for logs', () => {
|
|
29
|
-
execSync(`pm2 logs ${processName}`, {
|
|
30
|
-
stdio: "inherit",
|
|
31
|
-
});
|
|
32
|
-
})
|
|
33
|
-
.command("stop", 'Stop the server', () => {
|
|
34
|
-
execSync(`pm2 stop ${processName}`, {
|
|
35
|
-
stdio: "inherit",
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
.command("unregister", "Unregister custom protocol handler", () => {
|
|
39
|
-
if (os.platform() === "win32") {
|
|
40
|
-
require(path.join(__dirname, "..", "dist", "code", "register.windows.js")).unregister();
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
.command(["version"], 'Show version', () => {
|
|
44
|
-
console.log(`Data ramen CLI version: ${scriptPkg().version}`);
|
|
45
|
-
console.log(`Data ramen local server version: ${serverPkg().version}`);
|
|
46
|
-
})
|
|
47
|
-
.parse(isBrowserStart() ? getBrowserArgs() : yargsHelpers.hideBin(process.argv));
|
|
48
|
-
|
|
49
|
-
function shouldInstall () {
|
|
50
|
-
try {
|
|
51
|
-
const installedProxyPkjJson = serverPkg();
|
|
52
|
-
|
|
53
|
-
if (!installedProxyPkjJson) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const thisProxyPkgJson = fs.readJsonSync(path.join(__dirname, '..', 'dist', "package.json"));
|
|
58
|
-
return installedProxyPkjJson.version !== thisProxyPkgJson.version; // version are different, install new code
|
|
59
|
-
} catch (e) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function isBrowserStart () {
|
|
65
|
-
return !!process.argv[2]?.startsWith(protocol);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getBrowserArgs () {
|
|
69
|
-
const str = (process.argv[2] || '').replace(protocol, ''); // remove protocol
|
|
70
|
-
console.log("Browser args", str.split("/"));
|
|
71
|
-
return str.split("/");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function checkPm2 () {
|
|
75
|
-
try {
|
|
76
|
-
execSync(`pm2 -v`, {
|
|
77
|
-
stdio: "inherit"
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return true;
|
|
81
|
-
} catch (e) {
|
|
82
|
-
console.error("Library does not exist");
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function installPm2 () {
|
|
88
|
-
console.log("Install Pm2");
|
|
89
|
-
execSync(`npm i -g pm2`, {
|
|
90
|
-
stdio: "inherit"
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function stopExisting () {
|
|
95
|
-
try {
|
|
96
|
-
execSync(`pm2 stop "${processName}"`);
|
|
97
|
-
} catch (e) {}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// function registerServer () {
|
|
101
|
-
// try {
|
|
102
|
-
// if (os.platform() === "win32") {
|
|
103
|
-
// require(path.join(__dirname, "..", "dist", "code", "register.windows.js")).register();
|
|
104
|
-
// }
|
|
105
|
-
//
|
|
106
|
-
// console.log("Custom protocol handler registered");
|
|
107
|
-
// } catch (e) {
|
|
108
|
-
// console.log("Failed to register server", e);
|
|
109
|
-
// }
|
|
110
|
-
// }
|
|
111
|
-
|
|
112
|
-
function installServer () {
|
|
113
|
-
console.log("Installing Server");
|
|
114
|
-
// copy code
|
|
115
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "code"), path.join(filesPath, "code"));
|
|
116
|
-
// copy default env (do not override existing .env)
|
|
117
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "env", ".env.default"), path.join(filesPath, "env", ".env.default"));
|
|
118
|
-
// copy package.json
|
|
119
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "package.json"), path.join(filesPath, "package.json"));
|
|
120
|
-
|
|
121
|
-
execSync(`npm i`, {
|
|
122
|
-
stdio: "inherit",
|
|
123
|
-
cwd: filesPath,
|
|
124
|
-
});
|
|
125
|
-
console.log("Server installed");
|
|
126
|
-
|
|
127
|
-
// registerServer();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function start () {
|
|
131
|
-
stopExisting();
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
if (shouldInstall()) {
|
|
135
|
-
installServer();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log("Starting local server");
|
|
139
|
-
const appPkg = fs.readJsonSync(path.join(filesPath, "package.json"));
|
|
140
|
-
execSync(`pm2 start "${appPkg.main}" --name "${processName}" --no-autorestart`, {
|
|
141
|
-
stdio: "inherit",
|
|
142
|
-
cwd: filesPath,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
console.log(`Local server will be available in a couple of seconds`);
|
|
146
|
-
console.log(`You can close this window`);
|
|
147
|
-
console.log(`Visit https://app.dataramen.xyz`);
|
|
148
|
-
} catch (e) {
|
|
149
|
-
console.error(`Failed to start local server`, e);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function useJson (path) {
|
|
154
|
-
let file = undefined;
|
|
155
|
-
|
|
156
|
-
function get () {
|
|
157
|
-
try {
|
|
158
|
-
if (!file) {
|
|
159
|
-
file = fs.readJsonSync(path);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return file;
|
|
163
|
-
} catch (e) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return get;
|
|
169
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var C=Object.create;var u=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var T=(o,r,n,d)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of I(r))!L.call(o,a)&&a!==n&&u(o,a,{get:()=>r[a],enumerable:!(d=$(r,a))||d.enumerable});return o};var l=(o,r,n)=>(n=o!=null?C(J(o)):{},T(r||!o||!o.__esModule?u(n,"default",{value:o,enumerable:!0}):n,o));var O=l(require("yargs")),w=require("yargs/helpers");var v=require("node:os"),P=require("node:path"),D=(0,v.homedir)();var s="@dataramen/local-server",t=(0,P.join)(D,".dataramen",".runtime","server");var j=require("node:path"),A=l(require("fs-extra")),f=require("node:child_process");var h=l(require("fs-extra")),g=require("node:path");function S(o){let r;function n(){try{return r||(r=h.readJsonSync(o)),r}catch{return}}return n}var y=S((0,g.join)(__dirname,"..","package.json")),p=S((0,g.join)(t,"package.json"));var i=l(require("fs-extra")),e=require("node:path"),c=require("node:child_process");function x(){try{let o=p();if(!o)return!0;let r=i.readJsonSync((0,e.join)(__dirname,"..","dist","package.json"));return o.version!==r.version}catch{return!0}}function _(){try{return(0,c.execSync)("pm2 -v",{stdio:"inherit"}),!0}catch{return console.error("Library does not exist"),!1}}function E(){console.log("Install Pm2"),(0,c.execSync)("npm i -g pm2",{stdio:"inherit"})}function k(){try{(0,c.execSync)(`pm2 stop "${s}"`)}catch{}}function R(){console.log("Installing Server"),i.copySync((0,e.join)(__dirname,"..","dist","code"),(0,e.join)(t,"code")),i.copySync((0,e.join)(__dirname,"..","dist","env",".env.default"),(0,e.join)(t,"env",".env.default")),i.copySync((0,e.join)(__dirname,"..","dist","package.json"),(0,e.join)(t,"package.json")),(0,c.execSync)("npm i",{stdio:"inherit",cwd:t}),console.log("Server installed")}function H(){_()||E(),k();try{x()&&R(),console.log("Starting local server");let r=A.readJsonSync((0,j.join)(t,"package.json"));(0,f.execSync)(`pm2 start "${r.main}" --name "${s}" --no-autorestart`,{stdio:"inherit",cwd:t}),console.log("Local server will be available in a couple of seconds"),console.log("You can close this window"),console.log("Visit https://app.dataramen.xyz")}catch(r){console.error("Failed to start local server",r)}}function V(){(0,f.execSync)(`pm2 logs ${s}`,{stdio:"inherit"})}function b(){(0,f.execSync)(`pm2 stop ${s}`,{stdio:"inherit"})}function B(){console.log(`Data ramen CLI version: ${y().version}`),console.log(`Data ramen local server version: ${p().version}`)}var m={start:H,logs:V,stop:b,version:B};(0,O.default)((0,w.hideBin)(process.argv)).command("start","Default command, start/restart the server",m.start).command(["logs"],"Listen for logs",m.logs).command("stop","Stop the server",m.stop).command(["version"],"Show version",m.version).parse();
|
package/dist/README.md
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# 🍜 Data Ramen CLI
|
|
2
|
+
|
|
3
|
+
**Your cozy corner for exploring and working with SQL databases — no query writing required (unless you want to).**
|
|
4
|
+
|
|
5
|
+
Data Ramen CLI lets you launch a powerful, local database exploration app that connects effortlessly to MySQL and PostgreSQL databases. Browse schemas, follow relationships, insert data, and even write raw SQL — all from your browser.
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
- 🔌 **Connect to PostgreSQL & MySQL**
|
|
10
|
+
Securely plug in your credentials — you're ready to explore.
|
|
11
|
+
|
|
12
|
+
- 🧭 **Visual Schema Explorer**
|
|
13
|
+
Instantly browse tables, columns, types, primary/foreign keys, and relationships.
|
|
14
|
+
|
|
15
|
+
- 🧙 **No-SQL Querying**
|
|
16
|
+
Filter, sort, join, and summarize data using a beautiful, intuitive interface.
|
|
17
|
+
|
|
18
|
+
- 🔗 **Follow Relationships Easily**
|
|
19
|
+
Click to expand related records like nested tables.
|
|
20
|
+
|
|
21
|
+
- ✏️ **Insert & Update Visually**
|
|
22
|
+
Add or modify rows with a visual editor — no SQL errors.
|
|
23
|
+
|
|
24
|
+
- 💾 **Save Queries**
|
|
25
|
+
Save your favorite views for quick access later.
|
|
26
|
+
|
|
27
|
+
- ⌨️ **Raw SQL Mode Available**
|
|
28
|
+
Prefer typing? Drop into SQL mode anytime and run your own queries.
|
|
29
|
+
|
|
30
|
+
## 🛠️ Installation
|
|
31
|
+
|
|
32
|
+
### 1. Install Node.js
|
|
33
|
+
|
|
34
|
+
Data Ramen requires Node.js **v22 or above**.
|
|
35
|
+
👉 [Download Node.js](https://nodejs.org/)
|
|
36
|
+
|
|
37
|
+
### 2. Install Data Ramen CLI
|
|
38
|
+
|
|
39
|
+
Open your terminal and run:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm i -g @dataramen/cli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 🖥️ Usage
|
|
46
|
+
|
|
47
|
+
### Start the Local Server
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
dataramen start
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Open the App
|
|
54
|
+
|
|
55
|
+
Go to https://app.dataramen.xyz in your browser. Your local server will connect automatically.
|
|
56
|
+
|
|
57
|
+
## 🔧 Additional CLI Commands
|
|
58
|
+
|
|
59
|
+
| Command | Description |
|
|
60
|
+
| ----------------- | --------------------------------- |
|
|
61
|
+
| `dataramen start` | Start the local Data Ramen server |
|
|
62
|
+
| `dataramen stop` | Stop the local server |
|
|
63
|
+
| `dataramen logs` | Show logs from the running server |
|
|
64
|
+
|
|
65
|
+
## ❤️ Made with love by [Oleksandr Demian](https://github.com/OleksandrDemian)
|
|
66
|
+
|
|
67
|
+
Visit [dataramen.xyz](https://dataramen.xyz) for more info.
|
package/dist/code/cli.js
CHANGED
|
@@ -1,169 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const path = require("node:path");
|
|
4
|
-
const yargs = require("yargs");
|
|
5
|
-
const yargsHelpers = require("yargs/helpers");
|
|
6
|
-
const os = require('node:os');
|
|
7
|
-
const fs = require("fs-extra");
|
|
8
|
-
|
|
9
|
-
const processName = "@dataramen/local-server";
|
|
10
|
-
|
|
11
|
-
const homeDir = os.homedir();
|
|
12
|
-
const filesPath = path.join(homeDir, ".dataramen", ".runtime", "server");
|
|
13
|
-
|
|
14
|
-
const scriptPkg = useJson(path.join(__dirname, "..", "package.json"));
|
|
15
|
-
const serverPkg = useJson(path.join(filesPath, "package.json"));
|
|
16
|
-
|
|
17
|
-
const protocol = "dataramen://";
|
|
18
|
-
|
|
19
|
-
yargs()
|
|
20
|
-
.command("start", 'Default command, start/restart the server', () => {
|
|
21
|
-
const hasPm2 = checkPm2();
|
|
22
|
-
if (!hasPm2) {
|
|
23
|
-
installPm2();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
start();
|
|
27
|
-
})
|
|
28
|
-
.command(["logs"], 'Listen for logs', () => {
|
|
29
|
-
execSync(`pm2 logs ${processName}`, {
|
|
30
|
-
stdio: "inherit",
|
|
31
|
-
});
|
|
32
|
-
})
|
|
33
|
-
.command("stop", 'Stop the server', () => {
|
|
34
|
-
execSync(`pm2 stop ${processName}`, {
|
|
35
|
-
stdio: "inherit",
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
.command("unregister", "Unregister custom protocol handler", () => {
|
|
39
|
-
if (os.platform() === "win32") {
|
|
40
|
-
require(path.join(__dirname, "..", "dist", "code", "register.windows.js")).unregister();
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
.command(["version"], 'Show version', () => {
|
|
44
|
-
console.log(`Data ramen CLI version: ${scriptPkg().version}`);
|
|
45
|
-
console.log(`Data ramen local server version: ${serverPkg().version}`);
|
|
46
|
-
})
|
|
47
|
-
.parse(isBrowserStart() ? getBrowserArgs() : yargsHelpers.hideBin(process.argv));
|
|
48
|
-
|
|
49
|
-
function shouldInstall () {
|
|
50
|
-
try {
|
|
51
|
-
const installedProxyPkjJson = serverPkg();
|
|
52
|
-
|
|
53
|
-
if (!installedProxyPkjJson) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const thisProxyPkgJson = fs.readJsonSync(path.join(__dirname, '..', 'dist', "package.json"));
|
|
58
|
-
return installedProxyPkjJson.version !== thisProxyPkgJson.version; // version are different, install new code
|
|
59
|
-
} catch (e) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function isBrowserStart () {
|
|
65
|
-
return !!process.argv[2]?.startsWith(protocol);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getBrowserArgs () {
|
|
69
|
-
const str = (process.argv[2] || '').replace(protocol, ''); // remove protocol
|
|
70
|
-
console.log("Browser args", str.split("/"));
|
|
71
|
-
return str.split("/");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function checkPm2 () {
|
|
75
|
-
try {
|
|
76
|
-
execSync(`pm2 -v`, {
|
|
77
|
-
stdio: "inherit"
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return true;
|
|
81
|
-
} catch (e) {
|
|
82
|
-
console.error("Library does not exist");
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function installPm2 () {
|
|
88
|
-
console.log("Install Pm2");
|
|
89
|
-
execSync(`npm i -g pm2`, {
|
|
90
|
-
stdio: "inherit"
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function stopExisting () {
|
|
95
|
-
try {
|
|
96
|
-
execSync(`pm2 stop "${processName}"`);
|
|
97
|
-
} catch (e) {}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// function registerServer () {
|
|
101
|
-
// try {
|
|
102
|
-
// if (os.platform() === "win32") {
|
|
103
|
-
// require(path.join(__dirname, "..", "dist", "code", "register.windows.js")).register();
|
|
104
|
-
// }
|
|
105
|
-
//
|
|
106
|
-
// console.log("Custom protocol handler registered");
|
|
107
|
-
// } catch (e) {
|
|
108
|
-
// console.log("Failed to register server", e);
|
|
109
|
-
// }
|
|
110
|
-
// }
|
|
111
|
-
|
|
112
|
-
function installServer () {
|
|
113
|
-
console.log("Installing Server");
|
|
114
|
-
// copy code
|
|
115
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "code"), path.join(filesPath, "code"));
|
|
116
|
-
// copy default env (do not override existing .env)
|
|
117
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "env", ".env.default"), path.join(filesPath, "env", ".env.default"));
|
|
118
|
-
// copy package.json
|
|
119
|
-
fs.copySync(path.join(__dirname, '..', 'dist', "package.json"), path.join(filesPath, "package.json"));
|
|
120
|
-
|
|
121
|
-
execSync(`npm i`, {
|
|
122
|
-
stdio: "inherit",
|
|
123
|
-
cwd: filesPath,
|
|
124
|
-
});
|
|
125
|
-
console.log("Server installed");
|
|
126
|
-
|
|
127
|
-
// registerServer();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function start () {
|
|
131
|
-
stopExisting();
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
if (shouldInstall()) {
|
|
135
|
-
installServer();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
console.log("Starting local server");
|
|
139
|
-
const appPkg = fs.readJsonSync(path.join(filesPath, "package.json"));
|
|
140
|
-
execSync(`pm2 start "${appPkg.main}" --name "${processName}" --no-autorestart`, {
|
|
141
|
-
stdio: "inherit",
|
|
142
|
-
cwd: filesPath,
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
console.log(`Local server will be available in a couple of seconds`);
|
|
146
|
-
console.log(`You can close this window`);
|
|
147
|
-
console.log(`Visit https://app.dataramen.xyz`);
|
|
148
|
-
} catch (e) {
|
|
149
|
-
console.error(`Failed to start local server`, e);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function useJson (path) {
|
|
154
|
-
let file = undefined;
|
|
155
|
-
|
|
156
|
-
function get () {
|
|
157
|
-
try {
|
|
158
|
-
if (!file) {
|
|
159
|
-
file = fs.readJsonSync(path);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return file;
|
|
163
|
-
} catch (e) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return get;
|
|
169
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var C=Object.create;var u=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var T=(o,r,n,d)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of I(r))!L.call(o,a)&&a!==n&&u(o,a,{get:()=>r[a],enumerable:!(d=$(r,a))||d.enumerable});return o};var l=(o,r,n)=>(n=o!=null?C(J(o)):{},T(r||!o||!o.__esModule?u(n,"default",{value:o,enumerable:!0}):n,o));var O=l(require("yargs")),w=require("yargs/helpers");var v=require("node:os"),P=require("node:path"),D=(0,v.homedir)();var s="@dataramen/local-server",t=(0,P.join)(D,".dataramen",".runtime","server");var j=require("node:path"),A=l(require("fs-extra")),f=require("node:child_process");var h=l(require("fs-extra")),g=require("node:path");function S(o){let r;function n(){try{return r||(r=h.readJsonSync(o)),r}catch{return}}return n}var y=S((0,g.join)(__dirname,"..","package.json")),p=S((0,g.join)(t,"package.json"));var i=l(require("fs-extra")),e=require("node:path"),c=require("node:child_process");function x(){try{let o=p();if(!o)return!0;let r=i.readJsonSync((0,e.join)(__dirname,"..","dist","package.json"));return o.version!==r.version}catch{return!0}}function _(){try{return(0,c.execSync)("pm2 -v",{stdio:"inherit"}),!0}catch{return console.error("Library does not exist"),!1}}function E(){console.log("Install Pm2"),(0,c.execSync)("npm i -g pm2",{stdio:"inherit"})}function k(){try{(0,c.execSync)(`pm2 stop "${s}"`)}catch{}}function R(){console.log("Installing Server"),i.copySync((0,e.join)(__dirname,"..","dist","code"),(0,e.join)(t,"code")),i.copySync((0,e.join)(__dirname,"..","dist","env",".env.default"),(0,e.join)(t,"env",".env.default")),i.copySync((0,e.join)(__dirname,"..","dist","package.json"),(0,e.join)(t,"package.json")),(0,c.execSync)("npm i",{stdio:"inherit",cwd:t}),console.log("Server installed")}function H(){_()||E(),k();try{x()&&R(),console.log("Starting local server");let r=A.readJsonSync((0,j.join)(t,"package.json"));(0,f.execSync)(`pm2 start "${r.main}" --name "${s}" --no-autorestart`,{stdio:"inherit",cwd:t}),console.log("Local server will be available in a couple of seconds"),console.log("You can close this window"),console.log("Visit https://app.dataramen.xyz")}catch(r){console.error("Failed to start local server",r)}}function V(){(0,f.execSync)(`pm2 logs ${s}`,{stdio:"inherit"})}function b(){(0,f.execSync)(`pm2 stop ${s}`,{stdio:"inherit"})}function B(){console.log(`Data ramen CLI version: ${y().version}`),console.log(`Data ramen local server version: ${p().version}`)}var m={start:H,logs:V,stop:b,version:B};(0,O.default)((0,w.hideBin)(process.argv)).command("start","Default command, start/restart the server",m.start).command(["logs"],"Listen for logs",m.logs).command("stop","Stop the server",m.stop).command(["version"],"Show version",m.version).parse();
|
package/dist/code/proxy.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";var rt=Object.create;var ee=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var ot=Object.getPrototypeOf,nt=Object.prototype.hasOwnProperty;var W=(a,e)=>()=>(e||a((e={exports:{}}).exports,e),e.exports);var it=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of st(e))!nt.call(a,s)&&s!==t&&ee(a,s,{get:()=>e[s],enumerable:!(r=at(e,s))||r.enumerable});return a};var P=(a,e,t)=>(t=a!=null?rt(ot(a)):{},it(e||!a||!a.__esModule?ee(t,"default",{value:a,enumerable:!0}):t,a));var De=W(_e=>{"use strict";Object.defineProperty(_e,"__esModule",{value:!0})});var ke=W(D=>{"use strict";Object.defineProperty(D,"__esModule",{value:!0});D.SQLManipulator=D.SQLBuilder=D.SQLParser=void 0;var v=class{constructor(e="mysql"){this.dialect=e}parse(e){let r=e.trim().replace(/;$/,"").replace(/\s+/g," "),s=r.toUpperCase(),o;if(s.startsWith("SELECT"))o=this.parseSelect(r);else if(s.startsWith("INSERT"))o=this.parseInsert(r);else if(s.startsWith("UPDATE"))o=this.parseUpdate(r);else if(s.startsWith("DELETE"))o=this.parseDelete(r);else throw new Error("Unsupported SQL statement type");return o.parameters=this.extractParameters(r),o}extractParameters(e){let t=/:(\w+)/g,r=[],s;for(;(s=t.exec(e))!==null;){let o=s[1];r.includes(o)||r.push(o)}return r}parseSelect(e){let t={type:"SELECT"},{cleanedSQL:r,subqueries:s}=this.extractSubqueries(e),o=r.match(/SELECT\s+(.*?)\s+FROM/i);if(o){let p=o[1].trim();t.columns=p==="*"?["*"]:this.parseColumns(p)}let n=r.match(/FROM\s+([^\s]+)(?:\s|$)/i);if(n&&(t.table=n[1]),!t.table){let p=r.match(/FROM\s+([^\s]+)(?:\s+(?:AS\s+)?([^\s]+))?(?:\s+(?:WHERE|JOIN|INNER|LEFT|RIGHT|FULL|GROUP|ORDER|LIMIT|OFFSET|HAVING)|$)/i);p&&(t.table=p[1])}t.joins=this.parseJoins(r);let c=r.match(/WHERE\s+(.*?)(?:\s+GROUP\s+BY|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);c&&(t.where=this.restoreSubqueries(c[1].trim(),s));let i=r.match(/ORDER\s+BY\s+(.*?)(?:\s+LIMIT|\s+OFFSET|$)/i);i&&(t.orderBy=this.parseOrderBy(i[1]));let d=r.match(/GROUP\s+BY\s+(.*?)(?:\s+HAVING|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);d&&(t.groupBy=this.parseColumns(d[1]));let l=r.match(/HAVING\s+(.*?)(?:\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);l&&(t.having=this.restoreSubqueries(l[1].trim(),s));let T=r.match(/LIMIT\s+(\d+)/i);T&&(t.limit=parseInt(T[1]));let m=r.match(/OFFSET\s+(\d+)/i);return m&&(t.offset=parseInt(m[1])),t}parseColumns(e){let t=[],r="",s=!1,o="",n=0;for(let c=0;c<e.length;c++){let i=e[c];!s&&(i==='"'||i==="'")?(s=!0,o=i,r+=i):s&&i===o?c+1<e.length&&e[c+1]===o?(r+=i+i,c++):(s=!1,o="",r+=i):!s&&i==="("?(n++,r+=i):!s&&i===")"?(n--,r+=i):!s&&i===","&&n===0?(t.push(r.trim()),r=""):r+=i}return r.trim()&&t.push(r.trim()),t}extractSubqueries(e){let t=new Map,r=e,s=0,o=/\(([^()]*(?:SELECT|INSERT|UPDATE|DELETE)[^()]*(?:\([^()]*\)[^()]*)*)\)/gi,n=!0;for(;n;)n=!1,r=r.replace(o,(c,i)=>{if(/\b(SELECT|INSERT|UPDATE|DELETE)\b/i.test(i)){let d=`__SUBQUERY_${s}__`;return t.set(d,c),s++,n=!0,d}return c});return{cleanedSQL:r,subqueries:t}}restoreSubqueries(e,t){let r=e;return t.forEach((s,o)=>{r=r.replace(new RegExp(o,"g"),s)}),r}parseJoins(e){let t=[],r=/((?:INNER|LEFT|RIGHT|FULL)?\s*JOIN)\s+(\w+(?:\s+\w+)?)\s+ON\s+(.*?)(?=\s+(?:INNER|LEFT|RIGHT|FULL)?\s*JOIN|\s+WHERE|\s+GROUP\s+BY|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/gi,s;for(;(s=r.exec(e))!==null;){let o=s[1].trim().toUpperCase(),n=s[2].trim(),c=s[3].trim(),i=n.split(/\s+/),d=i[0],l=i.length>1?i[1]:void 0,T;o.includes("LEFT")?T="LEFT":o.includes("RIGHT")?T="RIGHT":o.includes("FULL")?T="FULL":T="INNER",t.push({type:T,table:d,alias:l,on:c})}return t.length>0?t:[]}parseInsert(e){let t={type:"INSERT"},r=e.match(/INSERT\s+INTO\s+(\w+)\s*\(([^)]+(?:\([^)]*\)[^)]*)*)\)\s*VALUES\s*\((.+)\)\s*;?$/i);return r&&(t.table=r[1],t.columns=this.parseColumns(r[2]),t.values=this.parseColumns(r[3])),t}parseUpdate(e){let t={type:"UPDATE"},r=e.match(/UPDATE\s+(\w+)\s+SET\s+(.*?)(?:\s+WHERE|$)/i);r&&(t.table=r[1],t.set=this.parseSetClause(r[2]));let s=e.match(/WHERE\s+(.*?)$/i);return s&&(t.where=s[1].trim()),t}parseDelete(e){let t={type:"DELETE"},r=e.match(/DELETE\s+FROM\s+(\w+)(?:\s+WHERE\s+(.*?))?$/i);return r&&(t.table=r[1],r[2]&&(t.where=r[2].trim())),t}parseOrderBy(e){return e.split(",").map(r=>{let s=r.trim().split(" "),o=s.pop();return o==="DESC"||o==="desc"?{column:s.join(" "),direction:"DESC"}:o==="ASC"||o==="asc"?{column:s.join(" "),direction:"ASC"}:{column:s.join(" ")+" "+o,direction:"ASC"}})}parseSetClause(e){let t={};return this.parseColumns(e).forEach(s=>{let o=s.indexOf("=");if(o>0){let n=s.substring(0,o).trim(),c=s.substring(o+1).trim();t[n]=c}}),t}};D.SQLParser=v;var F=class{constructor(e,t="mysql"){this.parsed={...e},this.dialect=t}build(){switch(this.parsed.type){case"SELECT":return this.buildSelect();case"INSERT":return this.buildInsert();case"UPDATE":return this.buildUpdate();case"DELETE":return this.buildDelete();default:throw new Error("Unsupported SQL type")}}buildSelect(){let e="SELECT ";if(this.parsed.columns&&this.parsed.columns.length>0?e+=this.parsed.columns.join(", "):e+="*",this.parsed.table&&(e+=` FROM ${this.parsed.table}`),this.parsed.joins&&this.parsed.joins.length>0&&this.parsed.joins.forEach(t=>{e+=` ${t.type} JOIN ${t.table} ON ${t.on}`}),this.parsed.where&&(e+=` WHERE ${this.parsed.where}`),this.parsed.groupBy&&this.parsed.groupBy.length>0&&(e+=` GROUP BY ${this.parsed.groupBy.join(", ")}`),this.parsed.having&&(e+=` HAVING ${this.parsed.having}`),this.parsed.orderBy&&this.parsed.orderBy.length>0){let t=this.parsed.orderBy.reduce((s,o)=>(s[o.column]=o.direction,s),{}),r=Object.entries(t).map(([s,o])=>`${s} ${o}`);e+=` ORDER BY ${r.join(", ")}`}return this.parsed.limit!==void 0&&(e+=` LIMIT ${this.parsed.limit}`),this.parsed.offset!==void 0&&(this.dialect==="mysql"?e+=` OFFSET ${this.parsed.offset}`:e+=` OFFSET ${this.parsed.offset}`),e}buildInsert(){if(!this.parsed.table||!this.parsed.columns||!this.parsed.values)throw new Error("Invalid INSERT statement data");let e=this.parsed.columns.join(", "),t=this.parsed.values.join(", ");return`INSERT INTO ${this.parsed.table} (${e}) VALUES (${t})`}buildUpdate(){if(!this.parsed.table||!this.parsed.set)throw new Error("Invalid UPDATE statement data");let e=Object.entries(this.parsed.set).map(([r,s])=>`${r} = ${s}`),t=`UPDATE ${this.parsed.table} SET ${e.join(", ")}`;return this.parsed.where&&(t+=` WHERE ${this.parsed.where}`),t}buildDelete(){if(!this.parsed.table)throw new Error("Invalid DELETE statement data");let e=`DELETE FROM ${this.parsed.table}`;return this.parsed.where&&(e+=` WHERE ${this.parsed.where}`),e}};D.SQLBuilder=F;var X=class{constructor(e="mysql",t){this.dialect=e,this.parser=new v(e),this.parsed=t==="SELECT"?{type:"SELECT"}:this.parser.parse(t),this.parameters={}}setParameter(e,t){return this.parameters[e]=t,this}setParameters(e){return this.parameters={...this.parameters,...e},this}getParameter(e){return this.parameters[e]}getParameters(){return{...this.parameters}}clearParameters(){return this.parameters={},this}getRequiredParameters(){return this.parsed.parameters||[]}getMissingParameters(){let e=this.getRequiredParameters(),t=Object.keys(this.parameters);return e.filter(r=>!t.includes(r))}addWhereWithParam(e,t,r,s="AND"){let o=this.buildWhereConditionWithParam(e,t,r);return this.parsed.where?this.parsed.where+=` ${s} ${o}`:this.parsed.where=o,this.parsed.parameters||(this.parsed.parameters=[]),this.parsed.parameters.includes(r)||this.parsed.parameters.push(r),this}addWhere(e){let t=this.buildWhereCondition(e);if(this.parsed.where){let r=e.connector||"AND";this.parsed.where+=` ${r} ${t}`}else this.parsed.where=t;return this}addWhereRaw(e,t="AND"){return this.parsed.where?this.parsed.where+=` ${t} ${e}`:this.parsed.where=e,this}clearWhere(){return this.parsed.where=void 0,this}addOrderBy(...e){return this.parsed.orderBy||(this.parsed.orderBy=[]),this.parsed.orderBy.push(...e),this}clearOrderBy(){return this.parsed.orderBy=void 0,this}setLimit(e){return this.parsed.limit=e,this}setOffset(e){return this.parsed.offset=e,this}addGroupBy(e){this.parsed.groupBy||(this.parsed.groupBy=[]);let t=this.parsed.groupBy.findIndex(r=>r===e);return t>-1?this.parsed.groupBy[t]=e:this.parsed.groupBy.push(e),this}setHaving(e){return this.parsed.having=e,this}setTable(e){return this.parsed.table=e,this}addJoin(...e){if(this.parsed.type!=="SELECT")throw new Error("JOIN operations are only supported for SELECT queries");return this.parsed.joins||(this.parsed.joins=[]),this.parsed.joins.push(...e),this}selectColumns(e){if(this.parsed.type!=="SELECT")throw new Error("Column selection is only supported for SELECT queries");return this.parsed.columns=e,this}toSQL(e=!1,t=!1){let s=new F(this.parsed,this.dialect).build();return e&&(s=this.substituteParameters(s)),t&&(s+=";"),s}toParameterizedSQL(e=!1){let r=new F(this.parsed,this.dialect).build();return e?r+";":r}toExecutableSQL(e=!1){return this.toSQL(!0,e)}substituteParameters(e){let t=e;return Object.entries(this.parameters).forEach(([r,s])=>{let o=new RegExp(`:${r}\\b`,"g"),n=this.formatParameterValue(s);t=t.replace(o,n)}),t}formatParameterValue(e){return e==null?"NULL":typeof e=="string"?e:typeof e=="boolean"?this.dialect==="postgres"?e?"TRUE":"FALSE":e?"1":"0":Array.isArray(e)?`(${e.map(r=>this.formatParameterValue(r)).join(", ")})`:e instanceof Date?`'${e.toISOString().slice(0,19).replace("T"," ")}'`:String(e)}buildWhereConditionWithParam(e,t,r){switch(t){case"IS NULL":case"IS NOT NULL":return`${e} ${t}`;case"IN":case"NOT IN":return`${e} ${t} :${r}`;default:return`${e} ${t} :${r}`}}getParsed(){return{...this.parsed}}buildWhereCondition(e){let{column:t,operator:r,value:s}=e;switch(r){case"IS NULL":case"IS NOT NULL":return`${t} ${r}`;case"IN":case"NOT IN":let o=s?.map(i=>typeof i=="string"?`'${i}'`:i).join(", ");return`${t} ${r} (${o})`;default:let n=s?.[0],c=typeof n=="string"?`'${n}'`:n;return`${t} ${r} ${c}`}}};D.SQLManipulator=X});var Me=W(k=>{"use strict";var It=k&&k.__createBinding||(Object.create?function(a,e,t,r){r===void 0&&(r=t);var s=Object.getOwnPropertyDescriptor(e,t);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(a,r,s)}:function(a,e,t,r){r===void 0&&(r=t),a[r]=e[t]}),qe=k&&k.__exportStar||function(a,e){for(var t in a)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&It(e,a,t)};Object.defineProperty(k,"__esModule",{value:!0});qe(De(),k);qe(ke(),k)});var $=require("dotenv"),Q=require("node:path"),te=require("node:fs"),ct=(()=>{try{let a=(0,te.readFileSync)((0,Q.join)(__dirname,"..","package.json"),"utf8");return JSON.parse(a)}catch{return{version:"0.0.0"}}})();(0,$.config)({path:[(0,Q.join)(__dirname,"..","env",".env"),(0,Q.join)(__dirname,"..","env",".env.default")]});(0,$.populate)(process.env,{SERVER_VERSION:ct.version});var Ze=P(require("fastify")),Xe=P(require("@fastify/cors"));var u=class extends Error{constructor(t,r){super(r);this.status=t;this.message=r}};var ue=require("typeorm");var re=require("typeorm"),j=new re.EntitySchema({name:"DatabaseInspection",tableName:"db_inspection",columns:{id:{type:String,unique:!0,primary:!0,generated:"uuid"},tableName:{nullable:!0,type:String},columns:{type:"json",nullable:!0},createdAt:{type:"datetime",default:"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:"CURRENT_TIMESTAMP"}},relations:{datasource:{target:()=>"DataSource",type:"many-to-one",joinTable:!1,cascade:!0}}});var ae=require("typeorm"),H=new ae.EntitySchema({name:"Team",tableName:"teams",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"}},relations:{users:{type:"many-to-many",target:()=>"User",inverseSide:"teams"},workbooks:{type:"one-to-many",target:()=>"Workbook",inverseSide:"team"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"team"},datasources:{type:"one-to-many",target:()=>"DataSource",inverseSide:"team"}}});var se=require("typeorm"),Y=new se.EntitySchema({name:"User",tableName:"users",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},createdAt:{type:"datetime",default:"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:"CURRENT_TIMESTAMP"}},relations:{teams:{type:"many-to-many",target:()=>"Team",inverseSide:"users",joinTable:!0},settings:{type:"one-to-one",target:()=>"UserSettings",inverseSide:"user",joinColumn:!0},currentTeam:{type:"many-to-one",target:()=>"Team",joinColumn:!0}}});var oe=require("typeorm"),G=new oe.EntitySchema({name:"UserSettings",tableName:"user_settings",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},openAiToken:{type:String,nullable:!0},model:{type:String,default:"gpt-3.5-turbo"},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"}},relations:{user:{type:"one-to-one",target:()=>"User",inverseSide:"settings",joinColumn:!0}}});var ne=require("typeorm"),V=new ne.EntitySchema({name:"Workbook",tableName:"workbooks",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},path:{type:"uuid",generated:"uuid"},isTrash:{type:"boolean",default:!1},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"workbooks",joinColumn:!0}}});var ie=require("typeorm"),z=new ie.EntitySchema({name:"DataSource",tableName:"data_sources",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},dbUrl:{type:String},dbPort:{type:Number,nullable:!0},dbUser:{type:String},dbPassword:{type:String,nullable:!0},dbType:{type:String},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},name:{type:String},description:{type:String,nullable:!0},dbDatabase:{type:String},dbSchema:{type:String,nullable:!0},lastInspected:{type:"datetime",nullable:!0,default:()=>null},status:{type:String,nullable:!0}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"datasources",joinColumn:!0},inspections:{type:"one-to-many",target:()=>"DatabaseInspection",inverseSide:"datasource"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"dataSource"},owner:{type:"many-to-one",target:()=>"User",joinColumn:!0}}});var de=P(require("os"));var ce=require("typeorm"),K=new ce.EntitySchema({name:"Query",tableName:"query",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},opts:{type:"json",default:"{}"},isTrash:{type:Boolean,default:!1,nullable:!0},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"workbooks",joinColumn:!0},dataSource:{type:"many-to-one",target:()=>"DataSource",inverseSide:"queries",joinColumn:!0}}});function ut(){let a=process.env.TYPEORM_DATABASE;if(!a)throw new Error("Bad value for TYPEORM_DATABASE. Please check your config!");return a.startsWith("<home>")&&(a=a.replace("<home>",de.default.homedir())),a}var E=new ue.DataSource({type:process.env.TYPEORM_CONNECTION,database:ut(),synchronize:process.env.TYPEORM_SYNCHRONIZE==="true",host:process.env.TYPEORM_HOST,username:process.env.TYPEORM_USERNAME,password:process.env.TYPEORM_PASSWORD,port:process.env.TYPEORM_PORT?parseInt(process.env.TYPEORM_PORT):void 0,logging:process.env.TYPEORM_LOGGING==="true",entities:[j,z,H,Y,G,V,K]}),me=async()=>{if(!E.isInitialized)return E.initialize();throw new Error("Already initialized")},C=E.getRepository(j),g=E.getRepository(z),q=E.getRepository(H),R=E.getRepository(Y),O=E.getRepository(G),S=E.getRepository(V),I=E.getRepository(K);var x=P(require("node:fs/promises")),J=require("node:path"),pe=P(require("node:os")),dt=pe.default.homedir(),Z=(0,J.join)(dt,".dataramen",".runtime","files"),le=a=>(0,J.join)(Z,a),fe=async a=>x.default.readFile(le(a),{encoding:"utf8"}),ye=async(a,e)=>x.default.writeFile(le(a),e,{encoding:"utf8"});var he=async()=>{await mt()||await x.default.mkdir(Z,{recursive:!0})};async function mt(){try{return(await x.default.lstat(Z)).isDirectory()}catch{return!1}}var f=a=>(e,t,r)=>{a(e),r()};var ge=f(a=>{a.post("/workbook",async e=>({data:""}))});var y=(a,e)=>{let t=a.body;return e&&e(t),t},N=(a,e)=>{let t=a.query;return e&&e(t),t},h=(a,e)=>{let t=a.params;return e&&e(t),t};var _=a=>a.headers["phoenix-user-id"];var Te=a=>{if(!a.dbUrl)throw new u(400,"url is required");if(!a.dbUser)throw new u(400,"user is required");if(!a.dbType)throw new u(400,"type is required");if(!a.name)throw new u(400,"name is required");if(!a.dbDatabase)throw new u(400,"database is required")};var we=P(require("mysql2/promise"));var lt=({dbDatabase:a,dbPassword:e,dbUser:t,dbUrl:r})=>we.default.createConnection({host:r,user:t,database:a,password:e}),ft=async a=>{let e=`
|
|
1
|
+
"use strict";var ot=Object.create;var te=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var ct=Object.getPrototypeOf,ut=Object.prototype.hasOwnProperty;var Q=(a,e)=>()=>(e||a((e={exports:{}}).exports,e),e.exports);var dt=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of it(e))!ut.call(a,s)&&s!==t&&te(a,s,{get:()=>e[s],enumerable:!(r=nt(e,s))||r.enumerable});return a};var P=(a,e,t)=>(t=a!=null?ot(ct(a)):{},dt(e||!a||!a.__esModule?te(t,"default",{value:a,enumerable:!0}):t,a));var ke=Q(De=>{"use strict";Object.defineProperty(De,"__esModule",{value:!0})});var qe=Q(W=>{"use strict";Object.defineProperty(W,"__esModule",{value:!0});W.isString=void 0;var Ot=a=>typeof a=="string";W.isString=Ot});var Le=Q(D=>{"use strict";Object.defineProperty(D,"__esModule",{value:!0});D.SQLManipulator=D.SQLBuilder=D.SQLParser=void 0;var Me=qe(),j=class{constructor(e="mysql"){this.dialect=e}parse(e){let r=e.trim().replace(/;$/,"").replace(/\s+/g," "),s=r.toUpperCase(),o;if(s.startsWith("SELECT"))o=this.parseSelect(r);else if(s.startsWith("INSERT"))o=this.parseInsert(r);else if(s.startsWith("UPDATE"))o=this.parseUpdate(r);else if(s.startsWith("DELETE"))o=this.parseDelete(r);else throw new Error("Unsupported SQL statement type");return o.parameters=this.extractParameters(r),o}extractParameters(e){let t=/:(\w+)/g,r=[],s;for(;(s=t.exec(e))!==null;){let o=s[1];r.includes(o)||r.push(o)}return r}parseSelect(e){let t={type:"SELECT"},{cleanedSQL:r,subqueries:s}=this.extractSubqueries(e),o=r.match(/SELECT\s+(.*?)\s+FROM/i);if(o){let p=o[1].trim();t.columns=p==="*"?["*"]:this.parseColumns(p)}let n=r.match(/FROM\s+([^\s]+)(?:\s|$)/i);if(n&&(t.table=n[1]),!t.table){let p=r.match(/FROM\s+([^\s]+)(?:\s+(?:AS\s+)?([^\s]+))?(?:\s+(?:WHERE|JOIN|INNER|LEFT|RIGHT|FULL|GROUP|ORDER|LIMIT|OFFSET|HAVING)|$)/i);p&&(t.table=p[1])}t.joins=this.parseJoins(r);let i=r.match(/WHERE\s+(.*?)(?:\s+GROUP\s+BY|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);i&&(t.where=this.restoreSubqueries(i[1].trim(),s));let c=r.match(/ORDER\s+BY\s+(.*?)(?:\s+LIMIT|\s+OFFSET|$)/i);c&&(t.orderBy=this.parseOrderBy(c[1]));let d=r.match(/GROUP\s+BY\s+(.*?)(?:\s+HAVING|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);d&&(t.groupBy=this.parseColumns(d[1]));let l=r.match(/HAVING\s+(.*?)(?:\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);l&&(t.having=this.restoreSubqueries(l[1].trim(),s));let T=r.match(/LIMIT\s+(\d+)/i);T&&(t.limit=parseInt(T[1]));let m=r.match(/OFFSET\s+(\d+)/i);return m&&(t.offset=parseInt(m[1])),t}parseColumns(e){let t=[],r="",s=!1,o="",n=0;for(let i=0;i<e.length;i++){let c=e[i];!s&&(c==='"'||c==="'")?(s=!0,o=c,r+=c):s&&c===o?i+1<e.length&&e[i+1]===o?(r+=c+c,i++):(s=!1,o="",r+=c):!s&&c==="("?(n++,r+=c):!s&&c===")"?(n--,r+=c):!s&&c===","&&n===0?(t.push(r.trim()),r=""):r+=c}return r.trim()&&t.push(r.trim()),t}extractSubqueries(e){let t=new Map,r=e,s=0,o=/\(([^()]*(?:SELECT|INSERT|UPDATE|DELETE)[^()]*(?:\([^()]*\)[^()]*)*)\)/gi,n=!0;for(;n;)n=!1,r=r.replace(o,(i,c)=>{if(/\b(SELECT|INSERT|UPDATE|DELETE)\b/i.test(c)){let d=`__SUBQUERY_${s}__`;return t.set(d,i),s++,n=!0,d}return i});return{cleanedSQL:r,subqueries:t}}restoreSubqueries(e,t){let r=e;return t.forEach((s,o)=>{r=r.replace(new RegExp(o,"g"),s)}),r}parseJoins(e){let t=[],r=/((?:INNER|LEFT|RIGHT|FULL)?\s*JOIN)\s+(\w+(?:\s+\w+)?)\s+ON\s+(.*?)(?=\s+(?:INNER|LEFT|RIGHT|FULL)?\s*JOIN|\s+WHERE|\s+GROUP\s+BY|\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/gi,s;for(;(s=r.exec(e))!==null;){let o=s[1].trim().toUpperCase(),n=s[2].trim(),i=s[3].trim(),c=n.split(/\s+/),d=c[0],l=c.length>1?c[1]:void 0,T;o.includes("LEFT")?T="LEFT":o.includes("RIGHT")?T="RIGHT":o.includes("FULL")?T="FULL":T="INNER",t.push({type:T,table:d,alias:l,on:i})}return t.length>0?t:[]}parseInsert(e){let t={type:"INSERT"},r=e.match(/INSERT\s+INTO\s+(\w+)\s*\(([^)]+(?:\([^)]*\)[^)]*)*)\)\s*VALUES\s*\((.+)\)\s*;?$/i);return r&&(t.table=r[1],t.columns=this.parseColumns(r[2]),t.values=this.parseColumns(r[3])),t}parseUpdate(e){let t={type:"UPDATE"},r=e.match(/UPDATE\s+(\w+)\s+SET\s+(.*?)(?:\s+WHERE|$)/i);r&&(t.table=r[1],t.set=this.parseSetClause(r[2]));let s=e.match(/WHERE\s+(.*?)$/i);return s&&(t.where=s[1].trim()),t}parseDelete(e){let t={type:"DELETE"},r=e.match(/DELETE\s+FROM\s+(\w+)(?:\s+WHERE\s+(.*?))?$/i);return r&&(t.table=r[1],r[2]&&(t.where=r[2].trim())),t}parseOrderBy(e){return e.split(",").map(r=>{let s=r.trim().split(" "),o=s.pop();return o==="DESC"||o==="desc"?{column:s.join(" "),direction:"DESC"}:o==="ASC"||o==="asc"?{column:s.join(" "),direction:"ASC"}:{column:s.join(" ")+" "+o,direction:"ASC"}})}parseSetClause(e){let t={};return this.parseColumns(e).forEach(s=>{let o=s.indexOf("=");if(o>0){let n=s.substring(0,o).trim(),i=s.substring(o+1).trim();t[n]=i}}),t}};D.SQLParser=j;var F=class{constructor(e,t="mysql"){this.parsed={...e},this.dialect=t}build(){switch(this.parsed.type){case"SELECT":return this.buildSelect();case"INSERT":return this.buildInsert();case"UPDATE":return this.buildUpdate();case"DELETE":return this.buildDelete();default:throw new Error("Unsupported SQL type")}}buildSelect(){let e="SELECT ";if(this.parsed.columns&&this.parsed.columns.length>0?e+=this.parsed.columns.join(", "):e+="*",this.parsed.table&&(e+=` FROM ${this.parsed.table}`),this.parsed.joins&&this.parsed.joins.length>0&&this.parsed.joins.forEach(t=>{e+=` ${t.type} JOIN ${t.table} ON ${t.on}`}),this.parsed.where&&(e+=` WHERE ${this.parsed.where}`),this.parsed.groupBy&&this.parsed.groupBy.length>0&&(e+=` GROUP BY ${this.parsed.groupBy.join(", ")}`),this.parsed.having&&(e+=` HAVING ${this.parsed.having}`),this.parsed.orderBy&&this.parsed.orderBy.length>0){let t=this.parsed.orderBy.reduce((s,o)=>(s[o.column]=o.direction,s),{}),r=Object.entries(t).map(([s,o])=>`${s} ${o}`);e+=` ORDER BY ${r.join(", ")}`}return this.parsed.limit!==void 0&&(e+=` LIMIT ${this.parsed.limit}`),this.parsed.offset!==void 0&&(this.dialect==="mysql"?e+=` OFFSET ${this.parsed.offset}`:e+=` OFFSET ${this.parsed.offset}`),e}buildInsert(){if(!this.parsed.table||!this.parsed.columns||!this.parsed.values)throw new Error("Invalid INSERT statement data");let e=this.parsed.columns.join(", "),t=this.parsed.values.join(", ");return`INSERT INTO ${this.parsed.table} (${e}) VALUES (${t})`}buildUpdate(){if(!this.parsed.table||!this.parsed.set)throw new Error("Invalid UPDATE statement data");let e=Object.entries(this.parsed.set).map(([r,s])=>`${r} = ${s}`),t=`UPDATE ${this.parsed.table} SET ${e.join(", ")}`;return this.parsed.where&&(t+=` WHERE ${this.parsed.where}`),t}buildDelete(){if(!this.parsed.table)throw new Error("Invalid DELETE statement data");let e=`DELETE FROM ${this.parsed.table}`;return this.parsed.where&&(e+=` WHERE ${this.parsed.where}`),e}};D.SQLBuilder=F;var ee=class{constructor(e="mysql",t){this.dialect=e,this.parser=new j(e),this.parsed=t==="SELECT"?{type:"SELECT"}:this.parser.parse(t),this.parameters={}}setParameter(e,t){return this.parameters[e]=t,this}setParameters(e){return this.parameters={...this.parameters,...e},this}getParameter(e){return this.parameters[e]}getParameters(){return{...this.parameters}}clearParameters(){return this.parameters={},this}getRequiredParameters(){return this.parsed.parameters||[]}getMissingParameters(){let e=this.getRequiredParameters(),t=Object.keys(this.parameters);return e.filter(r=>!t.includes(r))}addWhereWithParam(e,t,r,s="AND"){let o=this.buildWhereConditionWithParam(e,t,r);return this.parsed.where?this.parsed.where+=` ${s} ${o}`:this.parsed.where=o,this.parsed.parameters||(this.parsed.parameters=[]),this.parsed.parameters.includes(r)||this.parsed.parameters.push(r),this}addWhere(e){let t=this.buildWhereCondition(e);if(this.parsed.where){let r=e.connector||"AND";this.parsed.where+=` ${r} ${t}`}else this.parsed.where=t;return this}addWhereRaw(e,t="AND"){return this.parsed.where?this.parsed.where+=` ${t} ${e}`:this.parsed.where=e,this}clearWhere(){return this.parsed.where=void 0,this}addOrderBy(...e){return this.parsed.orderBy||(this.parsed.orderBy=[]),this.parsed.orderBy.push(...e),this}clearOrderBy(){return this.parsed.orderBy=void 0,this}setLimit(e){return this.parsed.limit=e,this}setOffset(e){return this.parsed.offset=e,this}addGroupBy(e){this.parsed.groupBy||(this.parsed.groupBy=[]);let t=this.parsed.groupBy.findIndex(r=>r===e);return t>-1?this.parsed.groupBy[t]=e:this.parsed.groupBy.push(e),this}setHaving(e){return this.parsed.having=e,this}setTable(e){return this.parsed.table=e,this}addJoin(...e){if(this.parsed.type!=="SELECT")throw new Error("JOIN operations are only supported for SELECT queries");return this.parsed.joins||(this.parsed.joins=[]),this.parsed.joins.push(...e),this}selectColumns(e){if(this.parsed.type!=="SELECT")throw new Error("Column selection is only supported for SELECT queries");return this.parsed.columns=e,this}toSQL(e=!1,t=!1){let s=new F(this.parsed,this.dialect).build();return e&&(s=this.substituteParameters(s)),t&&(s+=";"),s}toParameterizedSQL(e=!1){let r=new F(this.parsed,this.dialect).build();return e?r+";":r}toExecutableSQL(e=!1){return this.toSQL(!0,e)}substituteParameters(e){let t=e;return Object.entries(this.parameters).forEach(([r,s])=>{let o=new RegExp(`:${r}\\b`,"g"),n=this.formatParameterValue(s);t=t.replace(o,n)}),t}formatParameterValue(e){return e==null?"NULL":typeof e=="string"?e:typeof e=="boolean"?this.dialect==="postgres"?e?"TRUE":"FALSE":e?"1":"0":Array.isArray(e)?`(${e.map(r=>this.formatParameterValue(r)).join(", ")})`:e instanceof Date?`'${e.toISOString().slice(0,19).replace("T"," ")}'`:String(e)}buildWhereConditionWithParam(e,t,r){switch(t){case"IS NULL":case"IS NOT NULL":return`${e} ${t}`;case"IN":case"NOT IN":return`${e} ${t} :${r}`;default:return`${e} ${t} :${r}`}}getParsed(){return{...this.parsed}}buildWhereCondition(e){let{column:t,operator:r,value:s}=e;switch(r){case"IS NULL":case"IS NOT NULL":return`${t} ${r}`;case"IN":case"NOT IN":let o=s?.map(c=>(0,Me.isString)(c)?`'${c}'`:c).join(", ");return`${t} ${r} (${o})`;default:let n=s?.[0],i;return(0,Me.isString)(n?.value)&&n?.isColumn!==!0?i=`'${n?.value}'`:i=n?.value,`${t} ${r} ${i}`}}};D.SQLManipulator=ee});var xe=Q(k=>{"use strict";var Pt=k&&k.__createBinding||(Object.create?function(a,e,t,r){r===void 0&&(r=t);var s=Object.getOwnPropertyDescriptor(e,t);(!s||("get"in s?!e.__esModule:s.writable||s.configurable))&&(s={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(a,r,s)}:function(a,e,t,r){r===void 0&&(r=t),a[r]=e[t]}),Ue=k&&k.__exportStar||function(a,e){for(var t in a)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&Pt(e,a,t)};Object.defineProperty(k,"__esModule",{value:!0});Ue(ke(),k);Ue(Le(),k)});var v=require("dotenv"),$=require("node:path"),re=require("node:fs"),mt=(()=>{try{let a=(0,re.readFileSync)((0,$.join)(__dirname,"..","package.json"),"utf8");return JSON.parse(a)}catch{return{version:"0.0.0"}}})();(0,v.config)({path:[(0,$.join)(__dirname,"..","env",".env"),(0,$.join)(__dirname,"..","env",".env.default")]});(0,v.populate)(process.env,{SERVER_VERSION:mt.version});var tt=P(require("fastify")),rt=P(require("@fastify/cors"));var u=class extends Error{constructor(t,r){super(r);this.status=t;this.message=r}};var de=require("typeorm");var ae=require("typeorm"),H=new ae.EntitySchema({name:"DatabaseInspection",tableName:"db_inspection",columns:{id:{type:String,unique:!0,primary:!0,generated:"uuid"},tableName:{nullable:!0,type:String},columns:{type:"json",nullable:!0},createdAt:{type:"datetime",default:"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:"CURRENT_TIMESTAMP"}},relations:{datasource:{target:()=>"DataSource",type:"many-to-one",joinTable:!1,cascade:!0}}});var se=require("typeorm"),Y=new se.EntitySchema({name:"Team",tableName:"teams",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"}},relations:{users:{type:"many-to-many",target:()=>"User",inverseSide:"teams"},workbooks:{type:"one-to-many",target:()=>"Workbook",inverseSide:"team"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"team"},datasources:{type:"one-to-many",target:()=>"DataSource",inverseSide:"team"}}});var oe=require("typeorm"),G=new oe.EntitySchema({name:"User",tableName:"users",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},createdAt:{type:"datetime",default:"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:"CURRENT_TIMESTAMP"}},relations:{teams:{type:"many-to-many",target:()=>"Team",inverseSide:"users",joinTable:!0},settings:{type:"one-to-one",target:()=>"UserSettings",inverseSide:"user",joinColumn:!0},currentTeam:{type:"many-to-one",target:()=>"Team",joinColumn:!0}}});var ne=require("typeorm"),V=new ne.EntitySchema({name:"UserSettings",tableName:"user_settings",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},openAiToken:{type:String,nullable:!0},model:{type:String,default:"gpt-3.5-turbo"},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"}},relations:{user:{type:"one-to-one",target:()=>"User",inverseSide:"settings",joinColumn:!0}}});var ie=require("typeorm"),z=new ie.EntitySchema({name:"Workbook",tableName:"workbooks",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},path:{type:"uuid",generated:"uuid"},isTrash:{type:"boolean",default:!1},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"workbooks",joinColumn:!0}}});var ce=require("typeorm"),K=new ce.EntitySchema({name:"DataSource",tableName:"data_sources",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},dbUrl:{type:String},dbPort:{type:Number,nullable:!0},dbUser:{type:String},dbPassword:{type:String,nullable:!0},dbType:{type:String},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},name:{type:String},description:{type:String,nullable:!0},dbDatabase:{type:String},dbSchema:{type:String,nullable:!0},lastInspected:{type:"datetime",nullable:!0,default:()=>null},status:{type:String,nullable:!0}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"datasources",joinColumn:!0},inspections:{type:"one-to-many",target:()=>"DatabaseInspection",inverseSide:"datasource"},queries:{type:"one-to-many",target:()=>"Query",inverseSide:"dataSource"},owner:{type:"many-to-one",target:()=>"User",joinColumn:!0}}});var me=P(require("os"));var ue=require("typeorm"),J=new ue.EntitySchema({name:"Query",tableName:"query",columns:{id:{type:"uuid",primary:!0,generated:"uuid"},name:{type:String},opts:{type:"json",default:"{}"},isTrash:{type:Boolean,default:!1,nullable:!0},createdAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP"},updatedAt:{type:"datetime",default:()=>"CURRENT_TIMESTAMP",onUpdate:"CURRENT_TIMESTAMP"}},relations:{team:{type:"many-to-one",target:()=>"Team",inverseSide:"workbooks",joinColumn:!0},dataSource:{type:"many-to-one",target:()=>"DataSource",inverseSide:"queries",joinColumn:!0}}});function pt(){let a=process.env.TYPEORM_DATABASE;if(!a)throw new Error("Bad value for TYPEORM_DATABASE. Please check your config!");return a.startsWith("<home>")&&(a=a.replace("<home>",me.default.homedir())),a}var E=new de.DataSource({type:process.env.TYPEORM_CONNECTION,database:pt(),synchronize:process.env.TYPEORM_SYNCHRONIZE==="true",host:process.env.TYPEORM_HOST,username:process.env.TYPEORM_USERNAME,password:process.env.TYPEORM_PASSWORD,port:process.env.TYPEORM_PORT?parseInt(process.env.TYPEORM_PORT):void 0,logging:process.env.TYPEORM_LOGGING==="true",entities:[H,K,Y,G,V,z,J]}),pe=async()=>{if(!E.isInitialized)return E.initialize();throw new Error("Already initialized")},C=E.getRepository(H),g=E.getRepository(K),q=E.getRepository(Y),R=E.getRepository(G),O=E.getRepository(V),S=E.getRepository(z),I=E.getRepository(J);var x=P(require("node:fs/promises")),Z=require("node:path"),le=P(require("node:os")),lt=le.default.homedir(),X=(0,Z.join)(lt,".dataramen",".runtime","files"),fe=a=>(0,Z.join)(X,a),ye=async a=>x.default.readFile(fe(a),{encoding:"utf8"}),he=async(a,e)=>x.default.writeFile(fe(a),e,{encoding:"utf8"});var ge=async()=>{await ft()||await x.default.mkdir(X,{recursive:!0})};async function ft(){try{return(await x.default.lstat(X)).isDirectory()}catch{return!1}}var f=a=>(e,t,r)=>{a(e),r()};var Te=f(a=>{a.post("/workbook",async e=>({data:""}))});var y=(a,e)=>{let t=a.body;return e&&e(t),t},_=(a,e)=>{let t=a.query;return e&&e(t),t},h=(a,e)=>{let t=a.params;return e&&e(t),t};var N=a=>a.headers["phoenix-user-id"];var Ee=a=>{if(!a.dbUrl)throw new u(400,"url is required");if(!a.dbUser)throw new u(400,"user is required");if(!a.dbType)throw new u(400,"type is required");if(!a.name)throw new u(400,"name is required");if(!a.dbDatabase)throw new u(400,"database is required")};var Se=P(require("mysql2/promise"));var ht=({dbDatabase:a,dbPassword:e,dbUser:t,dbUrl:r})=>Se.default.createConnection({host:r,user:t,database:a,password:e}),gt=async a=>{let e=`
|
|
2
2
|
SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
|
|
3
3
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
4
4
|
WHERE CONSTRAINT_NAME = 'PRIMARY'
|
|
5
5
|
ORDER BY TABLE_NAME, ORDINAL_POSITION;
|
|
6
|
-
`,[t]=await a.execute(e),r={};return t.forEach(s=>{let o=s.TABLE_NAME,n=s.COLUMN_NAME;r[o]||(r[o]=[]),r[o].push(n)}),r},
|
|
6
|
+
`,[t]=await a.execute(e),r={};return t.forEach(s=>{let o=s.TABLE_NAME,n=s.COLUMN_NAME;r[o]||(r[o]=[]),r[o].push(n)}),r},Tt=async a=>{let e=`
|
|
7
7
|
SELECT
|
|
8
8
|
TABLE_NAME AS table_name,
|
|
9
9
|
COLUMN_NAME AS field,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
WHERE
|
|
15
15
|
REFERENCED_TABLE_NAME IS NOT NULL
|
|
16
16
|
AND CONSTRAINT_SCHEMA = DATABASE();
|
|
17
|
-
`,[t]=await a.execute(e),r={};return Array.isArray(t)&&t.forEach(s=>{r[s.table_name]||(r[s.table_name]={}),r[s.table_name][s.field]={refTable:s.referenced_table,refField:s.referenced_field}}),r},
|
|
17
|
+
`,[t]=await a.execute(e),r={};return Array.isArray(t)&&t.forEach(s=>{r[s.table_name]||(r[s.table_name]={}),r[s.table_name][s.field]={refTable:s.referenced_table,refField:s.referenced_field}}),r},Et=async(a,e)=>{let r=(await e.query("SHOW TABLES"))[0],s=await Tt(e),o=await gt(e),n=r.map(async i=>{let c=Object.values(i)[0],d=`select COLUMN_NAME, DATA_TYPE from information_schema.columns where table_schema = '${a.dbDatabase}' and table_name = '${c}'`,[l]=await e.query(d),T=s[c];return{columns:l.map(m=>({name:m.COLUMN_NAME,type:m.DATA_TYPE,isPrimary:o[c]?.includes(m.COLUMN_NAME),ref:T?.[m.COLUMN_NAME]?{table:T[m.COLUMN_NAME].refTable,field:T[m.COLUMN_NAME].refField}:void 0})).sort((m,p)=>m.isPrimary&&p.isPrimary?m.name.localeCompare(p.name):m.isPrimary?-1:1),createdAt:new Date,tableName:c,updatedAt:new Date}});return Promise.all(n)},we=async(a,e,t)=>{try{console.log(`[MYSQL CONN] Query: ${a}`);let[r,s]=await e.query({sql:a,rowsAsArray:!0}),o=r?.constructor?.name;if(o==="ResultSetHeader"){let n=r;if(n.affectedRows>3&&t.allowBulkUpdate!==!0)throw new Error("[MYSQL CONN] Bulk update performed without permission.");return{columns:[{column:"affectedRows",alias:"Affected rows",full:"affectedRows"}],rows:[[n.affectedRows]],query:a}}else if(o==="Array"){let n=r;return{columns:s?.map(i=>({column:i.orgName||i.name,table:i.orgTable,alias:i.name,full:i.orgTable?i.orgTable+"."+i.orgName:i.name}))||[],rows:n,query:a}}throw new Error(`[MYSQL CONN] Unknown result type: ${o}`)}catch(r){throw console.error(r),r instanceof u?r:new u(400,r.message)}},wt=async(a,e)=>{await a.beginTransaction();try{let t=await e();return await a.commit(),console.log("[MYSQL CONN] Commit"),t}catch(t){throw await a.rollback(),console.warn(t.message),console.log("[MYSQL CONN] Rollback"),t}},be=async a=>{let e=await ht(a),t=!1;return{dbType:"mysql",dataSource:a,inspectSchema:()=>Et(a,e),executeQuery:(r,s)=>s.type==="SELECT"?we(r,e,s):wt(e,()=>we(r,e,s)),checkConnection:async()=>e.ping(),isClosed:()=>t,close:async()=>{if(!t)return t=!0,e.destroy()}}};var Ie=P(require("pg"));var St=async({dbDatabase:a,dbPassword:e,dbUser:t,dbUrl:r,dbPort:s})=>{let o=new Ie.default.Client({host:r,user:t,database:a,password:e,port:s,query_timeout:1e4});return await o.connect(),o},bt=async a=>{let t=await a.query(`
|
|
18
18
|
SELECT
|
|
19
19
|
kcu.table_name,
|
|
20
20
|
kcu.column_name,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
tc.constraint_type = 'PRIMARY KEY'
|
|
29
29
|
ORDER BY
|
|
30
30
|
kcu.table_name, kcu.ordinal_position;
|
|
31
|
-
`),r={};return t.rows.forEach(s=>{let o=s.table_name,n=s.column_name;r[o]||(r[o]=[]),r[o].push(n)}),r},
|
|
31
|
+
`),r={};return t.rows.forEach(s=>{let o=s.table_name,n=s.column_name;r[o]||(r[o]=[]),r[o].push(n)}),r},Rt=async a=>{let t=await a.query(`
|
|
32
32
|
SELECT
|
|
33
33
|
tc.table_name AS table_name,
|
|
34
34
|
kcu.column_name AS field,
|
|
@@ -43,21 +43,21 @@
|
|
|
43
43
|
ON ccu.constraint_name = tc.constraint_name
|
|
44
44
|
AND ccu.table_schema = tc.table_schema
|
|
45
45
|
WHERE tc.constraint_type = 'FOREIGN KEY';
|
|
46
|
-
`),r={};return t.rows.forEach(s=>{r[s.table_name]||(r[s.table_name]={}),r[s.table_name][s.field]={refTable:s.referenced_table,refField:s.referenced_field}}),r},
|
|
46
|
+
`),r={};return t.rows.forEach(s=>{r[s.table_name]||(r[s.table_name]={}),r[s.table_name][s.field]={refTable:s.referenced_table,refField:s.referenced_field}}),r},It=async(a,e)=>{let t=`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = '${a.dbSchema}'`,s=(await e.query(t)).rows,o=await Rt(e),n=await bt(e),i=s.map(async c=>{let d=Object.values(c)[0],l=`
|
|
47
47
|
SELECT column_name, data_type
|
|
48
48
|
FROM information_schema.columns
|
|
49
49
|
WHERE
|
|
50
50
|
table_name = '${d}' and
|
|
51
51
|
table_schema = '${a.dbSchema}'
|
|
52
|
-
`,{rows:T}=await e.query(l),m=o[d];return{columns:T.map(p=>({name:p.column_name,type:p.data_type,isPrimary:n[d]?.includes(p.column_name),ref:m?.[p.column_name]?{table:m[p.column_name].refTable,field:m[p.column_name].refField}:void 0})).sort((p,w)=>p.isPrimary&&w.isPrimary?p.name.localeCompare(w.name):p.isPrimary?-1:1),createdAt:new Date,tableName:d,updatedAt:new Date}});return Promise.all(
|
|
52
|
+
`,{rows:T}=await e.query(l),m=o[d];return{columns:T.map(p=>({name:p.column_name,type:p.data_type,isPrimary:n[d]?.includes(p.column_name),ref:m?.[p.column_name]?{table:m[p.column_name].refTable,field:m[p.column_name].refField}:void 0})).sort((p,w)=>p.isPrimary&&w.isPrimary?p.name.localeCompare(w.name):p.isPrimary?-1:1),createdAt:new Date,tableName:d,updatedAt:new Date}});return Promise.all(i)},Ct=async(a,e)=>{let t=`select relname, attname, concat(pg_class.oid, '-', attnum) as row_key
|
|
53
53
|
from pg_attribute
|
|
54
54
|
left join pg_class on pg_attribute.attrelid = pg_class.oid
|
|
55
55
|
where
|
|
56
56
|
concat(pg_class.oid, '-', attnum) IN (${a.join(", ")})
|
|
57
|
-
limit 25;`;return(await e.query(t)).rows.reduce((s,o)=>(s[o.row_key]={table:o.relname,column:o.attname},s),{})},
|
|
57
|
+
limit 25;`;return(await e.query(t)).rows.reduce((s,o)=>(s[o.row_key]={table:o.relname,column:o.attname},s),{})},Re=async(a,e,t)=>{try{console.log(`[PG CONN] Query: ${a}`);let{rows:r,fields:s,command:o,rowCount:n}=await e.query({text:a,rowMode:"array"});if(o==="UPDATE"||o==="INSERT"||o==="DELETE"){if(n!=null&&n>3&&t.allowBulkUpdate!==!0)throw new Error("[PG CONN] Bulk update performed without permission.");return{columns:[{column:"affectedRows",alias:"Affected rows",full:"affectedRows"}],rows:[[n]],query:a}}if(o==="SELECT"){let i=s.map(d=>`'${d.tableID}-${d.columnID}'`),c=await Ct(i,e);return{columns:s.map(d=>{let l=c[`${d.tableID}-${d.columnID}`];return{column:l?.column||d.name,alias:d.name,table:l?.table||"",full:l?l.table+"."+l.column:d.name}}),rows:r,query:a}}throw new Error(`[PG CONN] Unsupported command: ${o}`)}catch(r){throw r instanceof u?r:new u(400,r.message)}},At=async(a,e)=>{await a.query("BEGIN");try{let t=await e();return await a.query("COMMIT"),console.log("[PG CONN] Commit"),t}catch(t){throw await a.query("ROLLBACK"),console.log("[PG CONN] Rollback"),t}},Ce=async a=>{let e=await St(a),t=!1,r=!1,s=async o=>(r||await e.query(`SET search_path TO ${a.dbSchema}`),o());return{dbType:"postgres",dataSource:a,inspectSchema:()=>It(a,e),executeQuery:(o,n)=>s(()=>n.type==="SELECT"?Re(o,e,n):At(e,()=>Re(o,e,n))),checkConnection:async()=>{},isClosed:()=>t,close:async()=>{if(!t)return t=!0,e.end()}}};var M=async(a,e)=>{try{let t;if(a.dbType==="mysql")t=await be(a);else if(a.dbType==="postgres")t=await Ce(a);else throw new u(500,`Connection manager for ${a.dbType} not found`);return e.__connections?e.__connections.push(t):e.__connections=[t],t}catch(t){throw console.error(t),t instanceof u?t:t?.code==="ECONNREFUSED"?new u(500,"Failed to connect to the database"):new u(500,t.message)}};var Ae=f(a=>{a.get("/:id",async e=>{let{id:t}=h(e),r=await g.findOne({where:{id:t}});if(!r)throw new u(404,"Data source not found");return{data:r}}),a.get("/",async e=>{let{teamId:t}=_(e);return{data:await g.find({where:{team:{id:t}},order:{createdAt:"DESC"}})}}),a.post("/",async(e,t)=>{let{teamId:r,ownerId:s,...o}=y(e,Ee),n=g.create({...o,team:{id:r},owner:{id:s}}),i=await M(n,e);try{await i.checkConnection()}catch{throw new u(400,"Cannot connect to the database, please check datasource configuration")}return{data:await g.save(n)}}),a.put("/:id",async(e,t)=>{let{id:r}=h(e),s=y(e),o=await g.findOneBy({id:r});if(!o)throw new u(404,"Data source not found");let n=g.merge(o,s);return await g.save(n),{data:n}}),a.delete("/:id",async(e,t)=>E.transaction(async()=>{let{id:r}=h(e);await Promise.all([C.delete({datasource:{id:r}}),I.delete({dataSource:{id:r}})]),await g.delete({id:r})})),a.post("/:id/inspect",async(e,t)=>{let{id:r}=h(e),s=await g.findOneBy({id:r});if(!s)throw new Error("Data source not found");s.status="INSPECTING",await g.save(s);let n=await(await M(s,e)).inspectSchema();await C.delete({datasource:{id:r}}),await C.insert(n.sort().map(i=>C.create({tableName:i.tableName,columns:i.columns,datasource:{id:r}}))),s.status="READY",s.lastInspected=new Date,await g.save(s)}),a.get("/:id/inspections",async(e,t)=>{let{id:r}=h(e);return{data:await C.find({where:{datasource:{id:r}}})}})});var Oe=require("typeorm"),Pe=f(a=>{a.get("/team/:teamId/files",async(e,t)=>{let{teamId:r}=h(e),s={where:{team:{id:r}},order:{name:"ASC"},select:{id:!0,name:!0,updatedAt:!0}},[o=[],n=[],i=[]]=await Promise.all([g.find(s),S.find({...s,where:{...s.where,isTrash:!1}}),I.find({...s,where:{...s.where,isTrash:!1}})]);return{data:[...o.map(c=>({...c,type:"dataSource"})),...n.map(c=>({...c,type:"workbook"})),...i.map(c=>({...c,type:"query"}))]}}),a.get("/team/:teamId/trash",async e=>{let{teamId:t}=h(e);return{data:await E.query(`
|
|
58
58
|
select id, name, updatedAt
|
|
59
59
|
from workbooks
|
|
60
60
|
where teamId = '${t}' AND (isTrash = true OR isTrash IS NULL)
|
|
61
61
|
order by name asc
|
|
62
62
|
limit 100;
|
|
63
|
-
`)}}),a.get("/team/:teamId/tables",async e=>{let{teamId:t}=h(e),{search:r,size:s}=
|
|
63
|
+
`)}}),a.get("/team/:teamId/tables",async e=>{let{teamId:t}=h(e),{search:r,size:s}=_(e);return{data:(await C.find({where:{tableName:(0,Oe.Like)(`%${r}%`),datasource:{team:{id:t}}},relations:{datasource:!0},select:{id:!0,tableName:!0,datasource:{name:!0,id:!0}},order:{tableName:"ASC"},take:parseInt(s)||20})).map(n=>({name:n.tableName,id:n.id,dataSourceName:n.datasource?.name||"--",dataSourceId:n.datasource?.id||"--"}))}})});function B(a,e=void 0){try{if(a){let t=a.split("&"),r={};for(let s of t){let o=s.split(":");r[o[0]]=o[1]}return r}}catch{}return e}var _e=require("typeorm"),Ne=f(a=>{a.get("/",async e=>{let{dataSourceId:t,teamId:r,limit:s,orderBy:o,name:n}=_(e);if(!t&&!r)throw new u(400,"Either dsId or teamId is required");let i={isTrash:!1};return t&&(i.dataSource={id:t}),r&&(i.team={id:r}),n&&(i.name=(0,_e.Like)(`%${n}%`)),{data:await I.find({where:i,take:s,order:B(o,{createdAt:"DESC"})})}}),a.get("/:id",async e=>{let{id:t}=h(e),r=await I.findOne({where:{id:t},select:{dataSource:{id:!0}},relations:{dataSource:!0}});return r?{data:r}:{status:404,data:"Query not found"}}),a.post("/",async e=>{let t=y(e),r=await g.findOne({where:{id:t.dataSourceId},relations:{team:!0}});return{data:await I.save(I.create({name:t.name,isTrash:!1,opts:t.opts,team:{id:r?.team.id},dataSource:{id:t.dataSourceId}}))}}),a.patch("/:id",async e=>{let{id:t}=h(e),r=y(e);if(!(await I.update(t,r)).affected)throw new u(404,"Workbook not found");return{data:await I.findOneBy({id:t})}}),a.delete("/:id",async e=>E.transaction(async()=>{let{id:t}=h(e);if(!(await I.delete({id:t})).affected)return{status:404,data:"Workbook not found"}}))});var ve=P(xe()),Be=require("typeorm");function Fe(a,e,t){return`${a.replace(/;\s*$/,"").trim()} LIMIT ${e} OFFSET ${t}`}function Qe(a,e){return a.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(t,r)=>{if(!(r in e))throw new Error(`Missing value for SQL variable: ${r}`);return String(e[r])})}function $e(a){let t=a.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim().match(/^\s*(\(?\s*)*([a-zA-Z]+)/);if(!t)throw new u(400,"Failed to detect query type");return t[2].toUpperCase()}function _t(a){return a||20}function Nt(a){return a||0}function Dt(a){return a.includes(" ")&&!a.startsWith("'")?`\`${a}\``:a}var We=async(a,{table:e,variables:t,datasourceId:r,filters:s,joins:o,orderBy:n,size:i,page:c,columns:d,groupBy:l})=>{let T=await g.findOneBy({id:r}),m=[],p=[];if(!T)throw new u(404,"Data source not found");let w=new ve.SQLManipulator(T.dbType,"SELECT");if(w.setTable(e),t&&w.setParameters(t),w.getParsed().type!=="SELECT")throw new u(400,"Only SELECT queries are allowed in this endpoint");w.setLimit(i||20),w.setOffset(i*c),s?.forEach(b=>{w.addWhere(kt(b))}),o&&w.addJoin(...o),n&&w.addOrderBy(...n.map(b=>({...b,column:Dt(b.column)}))),d&&d.length>0&&w.selectColumns(d),l&&l.length>0&&l.forEach(b=>w.addGroupBy(b));let U=w.getParsed();U.table&&m.push(U.table),U.joins&&U.joins.length>0&&U.joins.forEach(b=>{m.push(b.table)});let at=await C.find({where:{tableName:(0,Be.In)(m),datasource:{id:r}}});for(let b of at)if(b.columns)for(let st of b.columns)p.push({column:st.name,table:b.tableName||""});return{...await(await M(T,a)).executeQuery(w.toExecutableSQL(),{type:w.getParsed().type,allowBulkUpdate:!1}),tables:m,allColumns:p}},je=async(a,{sql:e,variables:t,datasourceId:r,size:s,page:o})=>{let n=await g.findOneBy({id:r});if(!n)throw new u(404,"Data source not found");let i=$e(e),c=e;if(i==="SELECT"){if(c.match(/LIMIT|OFFSET/ig))throw new u(400,"Queries are automatically paginated, do not use LIMIT and/or OFFSET");let l=_t(s);c=Fe(c,l,l*Nt(o))}return t&&(c=Qe(c,t)),(await M(n,a)).executeQuery(c,{type:i,allowBulkUpdate:!1})};function kt(a){let e={...a};switch(e.operator){case"IS NULL":case"IS NOT NULL":e.value=void 0;break;case"LIKE":case"ILIKE":case"NOT LIKE":case"NOT ILIKE":e.value=e.value?.map(t=>({isColumn:t?.isColumn,value:`%${t?.value}%`}));break}return e}var He=f(a=>{a.post("/sql",async e=>{let t=y(e);return{data:await We(e,t)}}),a.post("/raw",async e=>{let t=y(e);return{data:await je(e,t)}})});var Ye=require("node:child_process"),Ge=f(a=>{a.get("/",async(e,t)=>({data:{active:!0,version:process.env.SERVER_VERSION}})),a.get("/logs",async()=>{(0,Ye.spawn)(`echo "${process.env.SERVER_VERSION}"`)})});var Ve=f(a=>{a.get("/",async(e,t)=>{let r=N(e);return{data:await q.find({where:{users:{id:r}}})}}),a.post("/",async(e,t)=>E.transaction(async()=>{let r=N(e),s=y(e),o=R.create();o.id=r;let n=q.create(s);return n.users=[o],await q.save(n),{data:n}}))});var ze=async a=>({email:"local@localhost",sub:"local"});async function Ke(){let a=q.create({name:"Personal"}),e=await R.save(R.create({}));return a.users=[e],await q.save(a),await R.update(e.id,{currentTeam:{id:a.id}}),R.findOne({where:{},relations:{currentTeam:!0}})}var Je=f(a=>{a.post("/register",async(e,t)=>{let r=await ze(e);if(!r?.email||!r?.sub)throw new u(400,"Invalid token");return{data:await Ke()}}),a.get("/",async(e,t)=>{let r=await R.findOne({where:{},relations:{currentTeam:!0}});return r?{data:r}:{data:await Ke()}}),a.put("/",async(e,t)=>{let r=N(e),s=y(e);if(!(await R.update(r,s)).affected)throw new u(404,"User not found");return{data:R.findOneBy({id:r})}}),a.put("/team",async(e,t)=>{let r=N(e),s=y(e);return await R.update(r,{currentTeam:{id:s.teamId}}),{data:await R.findOne({where:{id:r},relations:{currentTeam:!0}})}})});var qt="********************************************",Ze=a=>a.slice(0,4)+qt+a.slice(a.length-4);function Mt(a){let e=O.create(a);return a.openAiToken&&(e.openAiToken=Ze(a.openAiToken)),e}var Xe=f(a=>{a.get("/",async e=>{let t=N(e),r=await O.findOneBy({user:{id:t}});return r||(r=await O.save(O.create({user:{id:t},model:"gpt-4o"}))),{data:Mt(r)}}),a.patch("/",async e=>{let{settings:t}=y(e);if(!t.id)throw new u(400,"Settings id is required!");if(!(await O.update(t.id,t)).affected)throw new u(404,"You do not own these settings!");return{data:await O.findOneBy({id:t.id})}})});var et=f(a=>{a.get("/",async e=>{let{dsId:t,teamId:r,limit:s,orderBy:o}=_(e);if(!t&&!r)throw new u(400,"Either dsId or teamId is required");return{data:await S.find({where:{team:{id:r},isTrash:!1},take:s,order:B(o,{createdAt:"DESC"})})}}),a.get("/:id",async e=>{let{id:t}=h(e),r=await S.findOneBy({id:t});return r?{data:r}:{status:404,data:"Workbook not found"}}),a.post("/",async e=>{let t=y(e);return{data:await S.save(S.create({name:t.name,isTrash:!1,team:{id:t.teamId}}))}}),a.patch("/:id",async e=>{let{id:t}=h(e),r=y(e);if(!(await S.update(t,r)).affected)throw new u(404,"Workbook not found");return{data:await S.findOneBy({id:t})}}),a.delete("/:id",async e=>{let{id:t}=h(e);if(!(await S.delete({id:t})).affected)return{status:404,data:"Workbook not found"}}),a.get("/:id/content",async e=>{let{id:t}=h(e),r=await S.findOneBy({id:t});if(!r)throw new u(404,"Workbook not found");try{let s=await ye(r?.path);if(s)return{data:s}}catch{}return{data:""}}),a.patch("/:id/content",async e=>{let{id:t}=h(e),r=y(e),s=await S.findOneBy({id:t});if(!s?.path)throw new u(404,"Workbook not found");await he(s?.path,r.content),await S.update(s.id,{updatedAt:new Date})})});var L=(0,tt.default)(),Lt=process.env.PORT?parseInt(process.env.PORT):4466;function A(a,e){L.register(a,{prefix:e}),console.log("Registered "+e)}async function Ut(){await ge(),A(Te,"/api/chat"),A(Ae,"/api/data-sources"),A(Pe,"/api/project"),A(Ne,"/api/queries"),A(He,"/api/runner"),A(Ge,"/api/status"),A(Ve,"/api/teams"),A(Je,"/api/users"),A(Xe,"/api/user-settings"),A(et,"/api/workbooks"),L.register(rt.default,{origin:"*",methods:"*"}),L.addHook("onResponse",async a=>{a.__connections&&a.__connections.forEach(e=>{e.close()})}),L.setErrorHandler((a,e,t)=>{if(a instanceof u){console.error(a),t.status(a.status).send({error:a.message});return}else console.error(a),t.status(500).send({error:"Internal Server Error"})}),await L.after(),await pe(),L.listen({port:Lt,host:"0.0.0.0"},(a,e)=>{a&&(console.error(a),process.exit(1)),console.log(`Server listening at ${e}`)})}Ut();
|
package/dist/package.json
CHANGED
|
@@ -1 +1,20 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "@dataramen/local-server",
|
|
3
|
+
"version": "0.0.35",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "code/proxy.js",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@fastify/cors": "^11.0.1",
|
|
8
|
+
"dotenv": "^16.5.0",
|
|
9
|
+
"fast-glob": "^3.3.3",
|
|
10
|
+
"fastify": "^5.3.2",
|
|
11
|
+
"mysql2": "^3.14.1",
|
|
12
|
+
"pg": "^8.15.6",
|
|
13
|
+
"sqlite3": "^5.1.7",
|
|
14
|
+
"typeorm": "^0.3.23"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"fs-extra": "^11.3.0",
|
|
18
|
+
"yargs": "^18.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dataramen/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dataramen": "bin/run.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "node build.
|
|
9
|
+
"build": "node build.mjs",
|
|
10
|
+
"package": "node package.mjs"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
13
|
"fs-extra": "^11.3.0",
|
|
13
14
|
"yargs": "^18.0.0"
|
|
14
15
|
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"esbuild": "^0.25.5",
|
|
18
|
+
"@types/fs-extra": "^11.0.3"
|
|
19
|
+
},
|
|
15
20
|
"files": [
|
|
16
21
|
"dist/"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"cli",
|
|
25
|
+
"sql",
|
|
26
|
+
"postgresql",
|
|
27
|
+
"mysql",
|
|
28
|
+
"database",
|
|
29
|
+
"schema",
|
|
30
|
+
"query-builder",
|
|
31
|
+
"data-browser",
|
|
32
|
+
"db-explorer",
|
|
33
|
+
"crud",
|
|
34
|
+
"data-inspector",
|
|
35
|
+
"visual-sql",
|
|
36
|
+
"no-code-sql",
|
|
37
|
+
"dataramen",
|
|
38
|
+
"nodejs",
|
|
39
|
+
"pm2",
|
|
40
|
+
"local-server",
|
|
41
|
+
"interactive-sql"
|
|
17
42
|
]
|
|
18
43
|
}
|