Add CLI admin panel
This commit is contained in:
parent
35c4ca3853
commit
4ae7fcf6a5
105
CLAUDE.md
105
CLAUDE.md
@ -1,106 +1 @@
|
|||||||
|
|
||||||
Default to using Bun instead of Node.js.
|
Default to using Bun instead of Node.js.
|
||||||
|
|
||||||
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
||||||
- Use `bun test` instead of `jest` or `vitest`
|
|
||||||
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
||||||
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
||||||
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
||||||
- Bun automatically loads .env, so don't use dotenv.
|
|
||||||
|
|
||||||
## APIs
|
|
||||||
|
|
||||||
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
||||||
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
||||||
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
||||||
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
||||||
- `WebSocket` is built-in. Don't use `ws`.
|
|
||||||
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
||||||
- Bun.$`ls` instead of execa.
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Use `bun test` to run tests.
|
|
||||||
|
|
||||||
```ts#index.test.ts
|
|
||||||
import { test, expect } from "bun:test";
|
|
||||||
|
|
||||||
test("hello world", () => {
|
|
||||||
expect(1).toBe(1);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend
|
|
||||||
|
|
||||||
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
||||||
|
|
||||||
Server:
|
|
||||||
|
|
||||||
```ts#index.ts
|
|
||||||
import index from "./index.html"
|
|
||||||
|
|
||||||
Bun.serve({
|
|
||||||
routes: {
|
|
||||||
"/": index,
|
|
||||||
"/api/users/:id": {
|
|
||||||
GET: (req) => {
|
|
||||||
return new Response(JSON.stringify({ id: req.params.id }));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// optional websocket support
|
|
||||||
websocket: {
|
|
||||||
open: (ws) => {
|
|
||||||
ws.send("Hello, world!");
|
|
||||||
},
|
|
||||||
message: (ws, message) => {
|
|
||||||
ws.send(message);
|
|
||||||
},
|
|
||||||
close: (ws) => {
|
|
||||||
// handle close
|
|
||||||
}
|
|
||||||
},
|
|
||||||
development: {
|
|
||||||
hmr: true,
|
|
||||||
console: true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
||||||
|
|
||||||
```html#index.html
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<h1>Hello, world!</h1>
|
|
||||||
<script type="module" src="./frontend.tsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
With the following `frontend.tsx`:
|
|
||||||
|
|
||||||
```tsx#frontend.tsx
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
// import .css files directly and it works
|
|
||||||
import './index.css';
|
|
||||||
|
|
||||||
import { createRoot } from "react-dom/client";
|
|
||||||
|
|
||||||
const root = createRoot(document.body);
|
|
||||||
|
|
||||||
export default function Frontend() {
|
|
||||||
return <h1>Hello, world!</h1>;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.render(<Frontend />);
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, run index.ts
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bun --hot ./index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
|
||||||
|
|||||||
108
bun.lock
108
bun.lock
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bun-sqlite",
|
"name": "bun-sqlite",
|
||||||
@ -7,13 +8,14 @@
|
|||||||
"@inquirer/prompts": "^8.3.0",
|
"@inquirer/prompts": "^8.3.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"commander": "^14.0.3",
|
"commander": "^14.0.3",
|
||||||
"inquirer": "^13.3.0",
|
"inquirer": "^13.3.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"sqlite-vec": "^0.1.7-alpha.2",
|
"sqlite-vec": "^0.1.7-alpha.2",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
"@types/inquirer": "^9.0.9",
|
||||||
"@types/lodash": "^4.17.24",
|
"@types/lodash": "^4.17.24",
|
||||||
"@types/mysql": "^2.15.27",
|
"@types/mysql": "^2.15.27",
|
||||||
"@types/node": "^25.3.3",
|
"@types/node": "^25.3.3",
|
||||||
@ -24,13 +26,13 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
"@inquirer/ansi": ["@inquirer/ansi@2.0.4", "", {}, "sha512-DpcZrQObd7S0R/U3bFdkcT5ebRwbTTC4D3tCc1vsJizmgPLxNJBo+AAFmrZwe8zk30P2QzgzGWZ3Q9uJwWuhIg=="],
|
||||||
|
|
||||||
"@inquirer/checkbox": ["@inquirer/checkbox@5.1.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.5", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g=="],
|
"@inquirer/checkbox": ["@inquirer/checkbox@5.1.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.5", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g=="],
|
||||||
|
|
||||||
"@inquirer/confirm": ["@inquirer/confirm@6.0.8", "", { "dependencies": { "@inquirer/core": "^11.1.5", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw=="],
|
"@inquirer/confirm": ["@inquirer/confirm@6.0.8", "", { "dependencies": { "@inquirer/core": "^11.1.5", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw=="],
|
||||||
|
|
||||||
"@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
"@inquirer/core": ["@inquirer/core@11.1.7", "", { "dependencies": { "@inquirer/ansi": "^2.0.4", "@inquirer/figures": "^2.0.4", "@inquirer/type": "^4.0.4", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-1BiBNDk9btIwYIzNZpkikIHXWeNzNncJePPqwDyVMhXhD1ebqbpn1mKGctpoqAbzywZfdG0O4tvmsGIcOevAPQ=="],
|
||||||
|
|
||||||
"@inquirer/editor": ["@inquirer/editor@5.0.8", "", { "dependencies": { "@inquirer/core": "^11.1.5", "@inquirer/external-editor": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA=="],
|
"@inquirer/editor": ["@inquirer/editor@5.0.8", "", { "dependencies": { "@inquirer/core": "^11.1.5", "@inquirer/external-editor": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA=="],
|
||||||
|
|
||||||
@ -54,16 +56,20 @@
|
|||||||
|
|
||||||
"@inquirer/select": ["@inquirer/select@5.1.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.5", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q=="],
|
"@inquirer/select": ["@inquirer/select@5.1.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.5", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q=="],
|
||||||
|
|
||||||
"@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
"@inquirer/type": ["@inquirer/type@4.0.4", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PamArxO3cFJZoOzspzo6cxVlLeIftyBsZw/S9bKY5DzxqJVZgjoj1oP8d0rskKtp7sZxBycsoer1g6UeJV1BBA=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
||||||
|
|
||||||
|
"@types/inquirer": ["@types/inquirer@9.0.9", "", { "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" } }, "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw=="],
|
||||||
|
|
||||||
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
||||||
|
|
||||||
"@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="],
|
"@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
||||||
|
|
||||||
|
"@types/through": ["@types/through@0.0.33", "", { "dependencies": { "@types/node": "*" } }, "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ=="],
|
||||||
|
|
||||||
"bignumber.js": ["bignumber.js@9.0.0", "", {}, "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="],
|
"bignumber.js": ["bignumber.js@9.0.0", "", {}, "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
||||||
@ -88,7 +94,7 @@
|
|||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
"inquirer": ["inquirer@13.3.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.5", "@inquirer/prompts": "^8.3.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-APTrZe9IhrsshL0u2PgmEMLP3CXDBjZ99xh5dR2+sryOt5R+JGL0KNuaTTT2lW54B9eNQDMutPR05UYTL7Xb1Q=="],
|
"inquirer": ["inquirer@13.3.2", "", { "dependencies": { "@inquirer/ansi": "^2.0.4", "@inquirer/core": "^11.1.7", "@inquirer/prompts": "^8.3.2", "@inquirer/type": "^4.0.4", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-bh/OjBGxNR9qvfQj1n5bxtIF58mbOTp2InN5dKuwUK03dXcDGFsjlDinQRuXMZ4EGiJaFieUWHCAaxH2p7iUBw=="],
|
||||||
|
|
||||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
|
|
||||||
@ -135,5 +141,97 @@
|
|||||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
|
"@inquirer/checkbox/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/checkbox/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/checkbox/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/confirm/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/confirm/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/core/@inquirer/figures": ["@inquirer/figures@2.0.4", "", {}, "sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ=="],
|
||||||
|
|
||||||
|
"@inquirer/editor/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/editor/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/expand/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/expand/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/input/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/input/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/number/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/number/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/password/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/password/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/password/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/rawlist/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/rawlist/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/search/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/search/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"@inquirer/select/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/select/@inquirer/core": ["@inquirer/core@11.1.5", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A=="],
|
||||||
|
|
||||||
|
"@inquirer/select/@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts": ["@inquirer/prompts@8.3.2", "", { "dependencies": { "@inquirer/checkbox": "^5.1.2", "@inquirer/confirm": "^6.0.10", "@inquirer/editor": "^5.0.10", "@inquirer/expand": "^5.0.10", "@inquirer/input": "^5.0.10", "@inquirer/number": "^4.0.10", "@inquirer/password": "^5.0.10", "@inquirer/rawlist": "^5.2.6", "@inquirer/search": "^4.1.6", "@inquirer/select": "^5.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-yFroiSj2iiBFlm59amdTvAcQFvWS6ph5oKESls/uqPBect7rTU2GbjyZO2DqxMGuIwVA8z0P4K6ViPcd/cp+0w=="],
|
||||||
|
|
||||||
|
"@inquirer/confirm/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/editor/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/expand/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/input/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/number/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/rawlist/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"@inquirer/search/@inquirer/core/@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/checkbox": ["@inquirer/checkbox@5.1.2", "", { "dependencies": { "@inquirer/ansi": "^2.0.4", "@inquirer/core": "^11.1.7", "@inquirer/figures": "^2.0.4", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PubpMPO2nJgMufkoB3P2wwxNXEMUXnBIKi/ACzDUYfaoPuM7gSTmuxJeMscoLVEsR4qqrCMf5p0SiYGWnVJ8kw=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/confirm": ["@inquirer/confirm@6.0.10", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-tiNyA73pgpQ0FQ7axqtoLUe4GDYjNCDcVsbgcA5anvwg2z6i+suEngLKKJrWKJolT//GFPZHwN30binDIHgSgQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/editor": ["@inquirer/editor@5.0.10", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/external-editor": "^2.0.4", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-VJx4XyaKea7t8hEApTw5dxeIyMtWXre2OiyJcICCRZI4hkoHsMoCnl/KbUnJJExLbH9csLLHMVR144ZhFE1CwA=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/expand": ["@inquirer/expand@5.0.10", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-fC0UHJPXsTRvY2fObiwuQYaAnHrp3aDqfwKUJSdfpgv18QUG054ezGbaRNStk/BKD5IPijeMKWej8VV8O5Q/eQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/input": ["@inquirer/input@5.0.10", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-nvZ6qEVeX/zVtZ1dY2hTGDQpVGD3R7MYPLODPgKO8Y+RAqxkrP3i/3NwF3fZpLdaMiNuK0z2NaYIx9tPwiSegQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/number": ["@inquirer/number@4.0.10", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Ht8OQstxiS3APMGjHV0aYAjRAysidWdwurWEo2i8yI5xbhOBWqizT0+MU1S2GCcuhIBg+3SgWVjEoXgfhY+XaA=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/password": ["@inquirer/password@5.0.10", "", { "dependencies": { "@inquirer/ansi": "^2.0.4", "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QbNyvIE8q2GTqKLYSsA8ATG+eETo+m31DSR0+AU7x3d2FhaTWzqQek80dj3JGTo743kQc6mhBR0erMjYw5jQ0A=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/rawlist": ["@inquirer/rawlist@5.2.6", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-jfw0MLJ5TilNsa9zlJ6nmRM0ZFVZhhTICt4/6CU2Dv1ndY7l3sqqo1gIYZyMMDw0LvE1u1nzJNisfHEhJIxq5w=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/search": ["@inquirer/search@4.1.6", "", { "dependencies": { "@inquirer/core": "^11.1.7", "@inquirer/figures": "^2.0.4", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-3/6kTRae98hhDevENScy7cdFEuURnSpM3JbBNg8yfXLw88HgTOl+neUuy/l9W0No5NzGsLVydhBzTIxZP7yChQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/select": ["@inquirer/select@5.1.2", "", { "dependencies": { "@inquirer/ansi": "^2.0.4", "@inquirer/core": "^11.1.7", "@inquirer/figures": "^2.0.4", "@inquirer/type": "^4.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-kTK8YIkHV+f02y7bWCh7E0u2/11lul5WepVTclr3UMBtBr05PgcZNWfMa7FY57ihpQFQH/spLMHTcr0rXy50tA=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/checkbox/@inquirer/figures": ["@inquirer/figures@2.0.4", "", {}, "sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/editor/@inquirer/external-editor": ["@inquirer/external-editor@2.0.4", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Prenuv9C1PHj2Itx0BcAOVBTonz02Hc2Nd2DbU67PdGUaqn0nPCnV34oDyyoaZHnmfRxkpuhh/u51ThkrO+RdA=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/search/@inquirer/figures": ["@inquirer/figures@2.0.4", "", {}, "sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ=="],
|
||||||
|
|
||||||
|
"inquirer/@inquirer/prompts/@inquirer/select/@inquirer/figures": ["@inquirer/figures@2.0.4", "", {}, "sha512-eLBsjlS7rPS3WEhmOmh1znQ5IsQrxWzxWDxO51e4urv+iVrSnIHbq4zqJIOiyNdYLa+BVjwOtdetcQx1lWPpiQ=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
dist/commands/admin/index.d.ts
vendored
Normal file
2
dist/commands/admin/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
export default function (): Command;
|
||||||
41
dist/commands/admin/index.js
vendored
Normal file
41
dist/commands/admin/index.js
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import init from "../../functions/init";
|
||||||
|
import grabDBDir from "../../utils/grab-db-dir";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select } from "@inquirer/prompts";
|
||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import listTables from "./list-tables";
|
||||||
|
import runSQL from "./run-sql";
|
||||||
|
export default function () {
|
||||||
|
return new Command("admin")
|
||||||
|
.description("View Tables and Data, Run SQL Queries, Etc.")
|
||||||
|
.action(async () => {
|
||||||
|
const { config } = await init();
|
||||||
|
const { db_file_path } = grabDBDir({ config });
|
||||||
|
const db = new Database(db_file_path);
|
||||||
|
console.log(chalk.bold(chalk.blue("\nBun SQLite Admin\n")));
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const paradigm = await select({
|
||||||
|
message: "Choose an action:",
|
||||||
|
choices: [
|
||||||
|
{ name: "List Tables", value: "list_tables" },
|
||||||
|
{ name: "Run SQL", value: "run_sql" },
|
||||||
|
{ name: chalk.dim("✕ Exit"), value: "exit" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (paradigm === "exit")
|
||||||
|
break;
|
||||||
|
if (paradigm === "list_tables")
|
||||||
|
await listTables({ db });
|
||||||
|
if (paradigm === "run_sql")
|
||||||
|
await runSQL({ db });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
6
dist/commands/admin/list-tables.d.ts
vendored
Normal file
6
dist/commands/admin/list-tables.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
type Params = {
|
||||||
|
db: Database;
|
||||||
|
};
|
||||||
|
export default function listTables({ db }: Params): Promise<void>;
|
||||||
|
export {};
|
||||||
57
dist/commands/admin/list-tables.js
vendored
Normal file
57
dist/commands/admin/list-tables.js
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select } from "@inquirer/prompts";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
import showEntries from "./show-entries";
|
||||||
|
export default async function listTables({ db }) {
|
||||||
|
const tables = db
|
||||||
|
.query(`SELECT table_name FROM ${AppData["DbSchemaManagerTableName"]}`)
|
||||||
|
.all();
|
||||||
|
if (!tables.length) {
|
||||||
|
console.log(chalk.yellow("\nNo tables found.\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Level 1: table selection loop
|
||||||
|
while (true) {
|
||||||
|
const tableName = await select({
|
||||||
|
message: "Select a table:",
|
||||||
|
choices: [
|
||||||
|
...tables.map((t) => ({ name: t.table_name, value: t.table_name })),
|
||||||
|
{ name: chalk.dim("← Go Back"), value: "__back__" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (tableName === "__back__")
|
||||||
|
break;
|
||||||
|
// Level 2: action loop — stays here until "Go Back"
|
||||||
|
while (true) {
|
||||||
|
const action = await select({
|
||||||
|
message: `"${tableName}" — choose an action:`,
|
||||||
|
choices: [
|
||||||
|
{ name: "Show Entries", value: "entries" },
|
||||||
|
{ name: "Show Schema", value: "schema" },
|
||||||
|
{ name: chalk.dim("← Go Back"), value: "__back__" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (action === "__back__")
|
||||||
|
break;
|
||||||
|
if (action === "entries") {
|
||||||
|
await showEntries({ db, tableName });
|
||||||
|
}
|
||||||
|
if (action === "schema") {
|
||||||
|
const columns = db
|
||||||
|
.query(`PRAGMA table_info("${tableName}")`)
|
||||||
|
.all();
|
||||||
|
console.log(`\n${chalk.bold(`Schema for "${tableName}":`)} \n`);
|
||||||
|
console.table(columns.map((c) => ({
|
||||||
|
"#": c.cid,
|
||||||
|
Name: c.name,
|
||||||
|
Type: c.type,
|
||||||
|
"Not Null": c.notnull ? "YES" : "NO",
|
||||||
|
Default: c.dflt_value ?? "(none)",
|
||||||
|
"Primary Key": c.pk ? "YES" : "NO",
|
||||||
|
})));
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
dist/commands/admin/run-sql.d.ts
vendored
Normal file
6
dist/commands/admin/run-sql.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
type Params = {
|
||||||
|
db: Database;
|
||||||
|
};
|
||||||
|
export default function runSQL({ db }: Params): Promise<void>;
|
||||||
|
export {};
|
||||||
27
dist/commands/admin/run-sql.js
vendored
Normal file
27
dist/commands/admin/run-sql.js
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import { input } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
export default async function runSQL({ db }) {
|
||||||
|
const sql = await input({
|
||||||
|
message: "Enter SQL query:",
|
||||||
|
validate: (val) => val.trim().length > 0 || "Query cannot be empty",
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const isSelect = /^select/i.test(sql.trim());
|
||||||
|
if (isSelect) {
|
||||||
|
const rows = db.query(sql).all();
|
||||||
|
console.log(`\n${chalk.bold(`Result (${rows.length} row${rows.length !== 1 ? "s" : ""}):`)} \n`);
|
||||||
|
if (rows.length)
|
||||||
|
console.table(rows);
|
||||||
|
else
|
||||||
|
console.log(chalk.yellow("No rows returned.\n"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const result = db.run(sql);
|
||||||
|
console.log(chalk.green(`\nSuccess! Affected rows: ${result.changes}, Last insert ID: ${result.lastInsertRowid}\n`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(chalk.red(`\nSQL Error: ${error.message}\n`));
|
||||||
|
}
|
||||||
|
}
|
||||||
7
dist/commands/admin/show-entries.d.ts
vendored
Normal file
7
dist/commands/admin/show-entries.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
type Params = {
|
||||||
|
db: Database;
|
||||||
|
tableName: string;
|
||||||
|
};
|
||||||
|
export default function showEntries({ db, tableName }: Params): Promise<void>;
|
||||||
|
export {};
|
||||||
71
dist/commands/admin/show-entries.js
vendored
Normal file
71
dist/commands/admin/show-entries.js
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select, input } from "@inquirer/prompts";
|
||||||
|
const LIMIT = 50;
|
||||||
|
export default async function showEntries({ db, tableName }) {
|
||||||
|
let page = 0;
|
||||||
|
let searchField = null;
|
||||||
|
let searchTerm = null;
|
||||||
|
while (true) {
|
||||||
|
const offset = page * LIMIT;
|
||||||
|
const rows = searchTerm
|
||||||
|
? db
|
||||||
|
.query(`SELECT * FROM "${tableName}" WHERE "${searchField}" LIKE ? LIMIT ${LIMIT} OFFSET ${offset}`)
|
||||||
|
.all(`%${searchTerm}%`)
|
||||||
|
: db
|
||||||
|
.query(`SELECT * FROM "${tableName}" LIMIT ${LIMIT} OFFSET ${offset}`)
|
||||||
|
.all();
|
||||||
|
const countRow = (searchTerm
|
||||||
|
? db
|
||||||
|
.query(`SELECT COUNT(*) as count FROM "${tableName}" WHERE "${searchField}" LIKE ?`)
|
||||||
|
.get(`%${searchTerm}%`)
|
||||||
|
: db
|
||||||
|
.query(`SELECT COUNT(*) as count FROM "${tableName}"`)
|
||||||
|
.get());
|
||||||
|
const total = countRow.count;
|
||||||
|
const searchInfo = searchTerm
|
||||||
|
? chalk.dim(` · searching "${searchField}" = "${searchTerm}"`)
|
||||||
|
: "";
|
||||||
|
console.log(`\n${chalk.bold(tableName)} — Page ${page + 1}${searchInfo} (${rows.length} of ${total}):\n`);
|
||||||
|
if (rows.length)
|
||||||
|
console.table(rows);
|
||||||
|
else
|
||||||
|
console.log(chalk.yellow("No rows found."));
|
||||||
|
console.log();
|
||||||
|
const choices = [];
|
||||||
|
if (page > 0)
|
||||||
|
choices.push({ name: "← Previous Page", value: "prev" });
|
||||||
|
if (offset + rows.length < total)
|
||||||
|
choices.push({ name: "Next Page →", value: "next" });
|
||||||
|
choices.push({ name: "Search by Field", value: "search" });
|
||||||
|
if (searchTerm)
|
||||||
|
choices.push({ name: "Clear Search", value: "clear_search" });
|
||||||
|
choices.push({ name: chalk.dim("← Go Back"), value: "__back__" });
|
||||||
|
const action = await select({ message: "Navigate:", choices });
|
||||||
|
if (action === "__back__")
|
||||||
|
break;
|
||||||
|
if (action === "next")
|
||||||
|
page++;
|
||||||
|
if (action === "prev")
|
||||||
|
page--;
|
||||||
|
if (action === "clear_search") {
|
||||||
|
searchField = null;
|
||||||
|
searchTerm = null;
|
||||||
|
page = 0;
|
||||||
|
}
|
||||||
|
if (action === "search") {
|
||||||
|
const columns = db
|
||||||
|
.query(`PRAGMA table_info("${tableName}")`)
|
||||||
|
.all();
|
||||||
|
searchField = await select({
|
||||||
|
message: "Search by field:",
|
||||||
|
choices: columns.map((c) => ({ name: c.name, value: c.name })),
|
||||||
|
});
|
||||||
|
searchTerm = await input({
|
||||||
|
message: `Search term for "${searchField}":`,
|
||||||
|
validate: (v) => v.trim().length > 0 || "Cannot be empty",
|
||||||
|
});
|
||||||
|
page = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
dist/commands/index.js
vendored
2
dist/commands/index.js
vendored
@ -4,6 +4,7 @@ import schema from "./schema";
|
|||||||
import typedef from "./typedef";
|
import typedef from "./typedef";
|
||||||
import backup from "./backup";
|
import backup from "./backup";
|
||||||
import restore from "./restore";
|
import restore from "./restore";
|
||||||
|
import admin from "./admin";
|
||||||
/**
|
/**
|
||||||
* # Describe Program
|
* # Describe Program
|
||||||
*/
|
*/
|
||||||
@ -18,6 +19,7 @@ program.addCommand(schema());
|
|||||||
program.addCommand(typedef());
|
program.addCommand(typedef());
|
||||||
program.addCommand(backup());
|
program.addCommand(backup());
|
||||||
program.addCommand(restore());
|
program.addCommand(restore());
|
||||||
|
program.addCommand(admin());
|
||||||
/**
|
/**
|
||||||
* # Handle Unavailable Commands
|
* # Handle Unavailable Commands
|
||||||
*/
|
*/
|
||||||
|
|||||||
1
dist/data/app-data.d.ts
vendored
1
dist/data/app-data.d.ts
vendored
@ -2,4 +2,5 @@ export declare const AppData: {
|
|||||||
readonly ConfigFileName: "bun-sqlite.config.ts";
|
readonly ConfigFileName: "bun-sqlite.config.ts";
|
||||||
readonly MaxBackups: 10;
|
readonly MaxBackups: 10;
|
||||||
readonly DefaultBackupDirName: ".backups";
|
readonly DefaultBackupDirName: ".backups";
|
||||||
|
readonly DbSchemaManagerTableName: "__db_schema_manager__";
|
||||||
};
|
};
|
||||||
|
|||||||
1
dist/data/app-data.js
vendored
1
dist/data/app-data.js
vendored
@ -2,4 +2,5 @@ export const AppData = {
|
|||||||
ConfigFileName: "bun-sqlite.config.ts",
|
ConfigFileName: "bun-sqlite.config.ts",
|
||||||
MaxBackups: 10,
|
MaxBackups: 10,
|
||||||
DefaultBackupDirName: ".backups",
|
DefaultBackupDirName: ".backups",
|
||||||
|
DbSchemaManagerTableName: "__db_schema_manager__",
|
||||||
};
|
};
|
||||||
|
|||||||
3
dist/lib/sqlite/db-schema-manager.js
vendored
3
dist/lib/sqlite/db-schema-manager.js
vendored
@ -2,6 +2,7 @@
|
|||||||
import { Database } from "bun:sqlite";
|
import { Database } from "bun:sqlite";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import DbClient from ".";
|
import DbClient from ".";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
// Schema Manager Class
|
// Schema Manager Class
|
||||||
class SQLiteSchemaManager {
|
class SQLiteSchemaManager {
|
||||||
db;
|
db;
|
||||||
@ -10,7 +11,7 @@ class SQLiteSchemaManager {
|
|||||||
db_schema;
|
db_schema;
|
||||||
constructor({ schema, recreate_vector_table = false, }) {
|
constructor({ schema, recreate_vector_table = false, }) {
|
||||||
this.db = DbClient;
|
this.db = DbClient;
|
||||||
this.db_manager_table_name = "__db_schema_manager__";
|
this.db_manager_table_name = AppData["DbSchemaManagerTableName"];
|
||||||
this.db.run("PRAGMA foreign_keys = ON;");
|
this.db.run("PRAGMA foreign_keys = ON;");
|
||||||
this.recreate_vector_table = recreate_vector_table;
|
this.recreate_vector_table = recreate_vector_table;
|
||||||
this.createDbManagerTable();
|
this.createDbManagerTable();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@moduletrace/bun-sqlite",
|
"name": "@moduletrace/bun-sqlite",
|
||||||
"version": "1.0.12",
|
"version": "1.0.13",
|
||||||
"description": "SQLite manager for Bun",
|
"description": "SQLite manager for Bun",
|
||||||
"author": "Benjamin Toby",
|
"author": "Benjamin Toby",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
"@types/inquirer": "^9.0.9",
|
||||||
"@types/lodash": "^4.17.24",
|
"@types/lodash": "^4.17.24",
|
||||||
"@types/mysql": "^2.15.27",
|
"@types/mysql": "^2.15.27",
|
||||||
"@types/node": "^25.3.3"
|
"@types/node": "^25.3.3"
|
||||||
@ -33,7 +34,7 @@
|
|||||||
"@inquirer/prompts": "^8.3.0",
|
"@inquirer/prompts": "^8.3.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"commander": "^14.0.3",
|
"commander": "^14.0.3",
|
||||||
"inquirer": "^13.3.0",
|
"inquirer": "^13.3.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"sqlite-vec": "^0.1.7-alpha.2"
|
"sqlite-vec": "^0.1.7-alpha.2"
|
||||||
|
|||||||
42
src/commands/admin/index.ts
Normal file
42
src/commands/admin/index.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import init from "../../functions/init";
|
||||||
|
import grabDBDir from "../../utils/grab-db-dir";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select } from "@inquirer/prompts";
|
||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import listTables from "./list-tables";
|
||||||
|
import runSQL from "./run-sql";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Command("admin")
|
||||||
|
.description("View Tables and Data, Run SQL Queries, Etc.")
|
||||||
|
.action(async () => {
|
||||||
|
const { config } = await init();
|
||||||
|
const { db_file_path } = grabDBDir({ config });
|
||||||
|
const db = new Database(db_file_path);
|
||||||
|
|
||||||
|
console.log(chalk.bold(chalk.blue("\nBun SQLite Admin\n")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const paradigm = await select({
|
||||||
|
message: "Choose an action:",
|
||||||
|
choices: [
|
||||||
|
{ name: "List Tables", value: "list_tables" },
|
||||||
|
{ name: "Run SQL", value: "run_sql" },
|
||||||
|
{ name: chalk.dim("✕ Exit"), value: "exit" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (paradigm === "exit") break;
|
||||||
|
if (paradigm === "list_tables") await listTables({ db });
|
||||||
|
if (paradigm === "run_sql") await runSQL({ db });
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
79
src/commands/admin/list-tables.ts
Normal file
79
src/commands/admin/list-tables.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select } from "@inquirer/prompts";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
import showEntries from "./show-entries";
|
||||||
|
|
||||||
|
type Params = { db: Database };
|
||||||
|
|
||||||
|
type ColumnInfo = {
|
||||||
|
cid: number;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
notnull: number;
|
||||||
|
dflt_value: string | null;
|
||||||
|
pk: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function listTables({ db }: Params) {
|
||||||
|
const tables = db
|
||||||
|
.query<{ table_name: string }, []>(
|
||||||
|
`SELECT table_name FROM ${AppData["DbSchemaManagerTableName"]}`,
|
||||||
|
)
|
||||||
|
.all();
|
||||||
|
|
||||||
|
if (!tables.length) {
|
||||||
|
console.log(chalk.yellow("\nNo tables found.\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level 1: table selection loop
|
||||||
|
while (true) {
|
||||||
|
const tableName = await select({
|
||||||
|
message: "Select a table:",
|
||||||
|
choices: [
|
||||||
|
...tables.map((t) => ({ name: t.table_name, value: t.table_name })),
|
||||||
|
{ name: chalk.dim("← Go Back"), value: "__back__" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tableName === "__back__") break;
|
||||||
|
|
||||||
|
// Level 2: action loop — stays here until "Go Back"
|
||||||
|
while (true) {
|
||||||
|
const action = await select({
|
||||||
|
message: `"${tableName}" — choose an action:`,
|
||||||
|
choices: [
|
||||||
|
{ name: "Show Entries", value: "entries" },
|
||||||
|
{ name: "Show Schema", value: "schema" },
|
||||||
|
{ name: chalk.dim("← Go Back"), value: "__back__" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (action === "__back__") break;
|
||||||
|
|
||||||
|
if (action === "entries") {
|
||||||
|
await showEntries({ db, tableName });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === "schema") {
|
||||||
|
const columns = db
|
||||||
|
.query<ColumnInfo, []>(`PRAGMA table_info("${tableName}")`)
|
||||||
|
.all();
|
||||||
|
|
||||||
|
console.log(`\n${chalk.bold(`Schema for "${tableName}":`)} \n`);
|
||||||
|
console.table(
|
||||||
|
columns.map((c) => ({
|
||||||
|
"#": c.cid,
|
||||||
|
Name: c.name,
|
||||||
|
Type: c.type,
|
||||||
|
"Not Null": c.notnull ? "YES" : "NO",
|
||||||
|
Default: c.dflt_value ?? "(none)",
|
||||||
|
"Primary Key": c.pk ? "YES" : "NO",
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/commands/admin/run-sql.ts
Normal file
34
src/commands/admin/run-sql.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import { input } from "@inquirer/prompts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
type Params = { db: Database };
|
||||||
|
|
||||||
|
export default async function runSQL({ db }: Params) {
|
||||||
|
const sql = await input({
|
||||||
|
message: "Enter SQL query:",
|
||||||
|
validate: (val) => val.trim().length > 0 || "Query cannot be empty",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const isSelect = /^select/i.test(sql.trim());
|
||||||
|
|
||||||
|
if (isSelect) {
|
||||||
|
const rows = db.query(sql).all();
|
||||||
|
console.log(
|
||||||
|
`\n${chalk.bold(`Result (${rows.length} row${rows.length !== 1 ? "s" : ""}):`)} \n`,
|
||||||
|
);
|
||||||
|
if (rows.length) console.table(rows);
|
||||||
|
else console.log(chalk.yellow("No rows returned.\n"));
|
||||||
|
} else {
|
||||||
|
const result = db.run(sql);
|
||||||
|
console.log(
|
||||||
|
chalk.green(
|
||||||
|
`\nSuccess! Affected rows: ${result.changes}, Last insert ID: ${result.lastInsertRowid}\n`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(chalk.red(`\nSQL Error: ${error.message}\n`));
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/commands/admin/show-entries.ts
Normal file
88
src/commands/admin/show-entries.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { Database } from "bun:sqlite";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { select, input } from "@inquirer/prompts";
|
||||||
|
|
||||||
|
type Params = { db: Database; tableName: string };
|
||||||
|
type ColumnInfo = { cid: number; name: string };
|
||||||
|
|
||||||
|
const LIMIT = 50;
|
||||||
|
|
||||||
|
export default async function showEntries({ db, tableName }: Params) {
|
||||||
|
let page = 0;
|
||||||
|
let searchField: string | null = null;
|
||||||
|
let searchTerm: string | null = null;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const offset = page * LIMIT;
|
||||||
|
|
||||||
|
const rows = searchTerm
|
||||||
|
? db
|
||||||
|
.query(
|
||||||
|
`SELECT * FROM "${tableName}" WHERE "${searchField}" LIKE ? LIMIT ${LIMIT} OFFSET ${offset}`,
|
||||||
|
)
|
||||||
|
.all(`%${searchTerm}%`)
|
||||||
|
: db
|
||||||
|
.query(
|
||||||
|
`SELECT * FROM "${tableName}" LIMIT ${LIMIT} OFFSET ${offset}`,
|
||||||
|
)
|
||||||
|
.all();
|
||||||
|
|
||||||
|
const countRow = (
|
||||||
|
searchTerm
|
||||||
|
? db
|
||||||
|
.query(
|
||||||
|
`SELECT COUNT(*) as count FROM "${tableName}" WHERE "${searchField}" LIKE ?`,
|
||||||
|
)
|
||||||
|
.get(`%${searchTerm}%`)
|
||||||
|
: db
|
||||||
|
.query(`SELECT COUNT(*) as count FROM "${tableName}"`)
|
||||||
|
.get()
|
||||||
|
) as { count: number };
|
||||||
|
|
||||||
|
const total = countRow.count;
|
||||||
|
const searchInfo = searchTerm
|
||||||
|
? chalk.dim(` · searching "${searchField}" = "${searchTerm}"`)
|
||||||
|
: "";
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`\n${chalk.bold(tableName)} — Page ${page + 1}${searchInfo} (${rows.length} of ${total}):\n`,
|
||||||
|
);
|
||||||
|
if (rows.length) console.table(rows);
|
||||||
|
else console.log(chalk.yellow("No rows found."));
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
const choices: { name: string; value: string }[] = [];
|
||||||
|
if (page > 0) choices.push({ name: "← Previous Page", value: "prev" });
|
||||||
|
if (offset + rows.length < total)
|
||||||
|
choices.push({ name: "Next Page →", value: "next" });
|
||||||
|
choices.push({ name: "Search by Field", value: "search" });
|
||||||
|
if (searchTerm)
|
||||||
|
choices.push({ name: "Clear Search", value: "clear_search" });
|
||||||
|
choices.push({ name: chalk.dim("← Go Back"), value: "__back__" });
|
||||||
|
|
||||||
|
const action = await select({ message: "Navigate:", choices });
|
||||||
|
|
||||||
|
if (action === "__back__") break;
|
||||||
|
if (action === "next") page++;
|
||||||
|
if (action === "prev") page--;
|
||||||
|
if (action === "clear_search") {
|
||||||
|
searchField = null;
|
||||||
|
searchTerm = null;
|
||||||
|
page = 0;
|
||||||
|
}
|
||||||
|
if (action === "search") {
|
||||||
|
const columns = db
|
||||||
|
.query<ColumnInfo, []>(`PRAGMA table_info("${tableName}")`)
|
||||||
|
.all();
|
||||||
|
searchField = await select({
|
||||||
|
message: "Search by field:",
|
||||||
|
choices: columns.map((c) => ({ name: c.name, value: c.name })),
|
||||||
|
});
|
||||||
|
searchTerm = await input({
|
||||||
|
message: `Search term for "${searchField}":`,
|
||||||
|
validate: (v) => v.trim().length > 0 || "Cannot be empty",
|
||||||
|
});
|
||||||
|
page = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import schema from "./schema";
|
|||||||
import typedef from "./typedef";
|
import typedef from "./typedef";
|
||||||
import backup from "./backup";
|
import backup from "./backup";
|
||||||
import restore from "./restore";
|
import restore from "./restore";
|
||||||
|
import admin from "./admin";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # Declare Global Variables
|
* # Declare Global Variables
|
||||||
@ -26,6 +27,7 @@ program.addCommand(schema());
|
|||||||
program.addCommand(typedef());
|
program.addCommand(typedef());
|
||||||
program.addCommand(backup());
|
program.addCommand(backup());
|
||||||
program.addCommand(restore());
|
program.addCommand(restore());
|
||||||
|
program.addCommand(admin());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # Handle Unavailable Commands
|
* # Handle Unavailable Commands
|
||||||
|
|||||||
@ -2,4 +2,5 @@ export const AppData = {
|
|||||||
ConfigFileName: "bun-sqlite.config.ts",
|
ConfigFileName: "bun-sqlite.config.ts",
|
||||||
MaxBackups: 10,
|
MaxBackups: 10,
|
||||||
DefaultBackupDirName: ".backups",
|
DefaultBackupDirName: ".backups",
|
||||||
|
DbSchemaManagerTableName: "__db_schema_manager__",
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
BUN_SQLITE_FieldSchemaType,
|
BUN_SQLITE_FieldSchemaType,
|
||||||
BUN_SQLITE_TableSchemaType,
|
BUN_SQLITE_TableSchemaType,
|
||||||
} from "../../types";
|
} from "../../types";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
|
||||||
// Schema Manager Class
|
// Schema Manager Class
|
||||||
class SQLiteSchemaManager {
|
class SQLiteSchemaManager {
|
||||||
@ -24,7 +25,7 @@ class SQLiteSchemaManager {
|
|||||||
recreate_vector_table?: boolean;
|
recreate_vector_table?: boolean;
|
||||||
}) {
|
}) {
|
||||||
this.db = DbClient;
|
this.db = DbClient;
|
||||||
this.db_manager_table_name = "__db_schema_manager__";
|
this.db_manager_table_name = AppData["DbSchemaManagerTableName"];
|
||||||
this.db.run("PRAGMA foreign_keys = ON;");
|
this.db.run("PRAGMA foreign_keys = ON;");
|
||||||
this.recreate_vector_table = recreate_vector_table;
|
this.recreate_vector_table = recreate_vector_table;
|
||||||
this.createDbManagerTable();
|
this.createDbManagerTable();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user