admindb 1.0.1 → 1.0.3
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/LICENSE +20 -20
- package/README.md +333 -325
- package/dist/db/database.d.ts +3 -2
- package/dist/db/database.js +12 -9
- package/dist/db/database.js.map +1 -1
- package/dist/db/manager.js +1 -1
- package/dist/db/manager.js.map +1 -1
- package/dist/public/css/input.css +179 -179
- package/dist/public/js/api.js +47 -47
- package/dist/public/js/app.js +120 -120
- package/dist/public/js/browse.js +123 -123
- package/dist/public/js/databases.js +45 -45
- package/dist/public/js/designer.js +205 -205
- package/dist/public/js/forms.js +244 -244
- package/dist/public/js/query.js +232 -232
- package/dist/public/js/schema.js +314 -314
- package/dist/public/js/triggers.js +155 -155
- package/dist/public/js/views.js +176 -176
- package/dist/views/layouts/main.hbs +49 -49
- package/dist/views/pages/databases.hbs +102 -102
- package/dist/views/pages/designer.hbs +81 -81
- package/dist/views/pages/error.hbs +12 -12
- package/dist/views/pages/form.hbs +62 -62
- package/dist/views/pages/home.hbs +123 -123
- package/dist/views/pages/query.hbs +94 -94
- package/dist/views/pages/schema.hbs +290 -290
- package/dist/views/pages/table.hbs +203 -203
- package/dist/views/pages/triggers.hbs +162 -162
- package/dist/views/pages/views.hbs +134 -134
- package/dist/views/partials/navbar.hbs +50 -50
- package/dist/views/partials/sidebar.hbs +128 -128
- package/package.json +66 -62
package/README.md
CHANGED
|
@@ -1,325 +1,333 @@
|
|
|
1
|
-
# AdminDB
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- **
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
- **
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- **
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
- **
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
](https://www.npmjs.com/package/admindb)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://github.com/MIbnEKhalid/admindb/actions/workflows/publish.yml)
|
|
8
|
+
[](https://www.npmjs.com/package/admindb)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
A browser-based SQLite database administration tool. Manage a SQLite database
|
|
12
|
+
entirely from the browser: browse tables, full CRUD, a visual table designer, an
|
|
13
|
+
arbitrary SQL query runner, saved named queries, SQL-dump export, a
|
|
14
|
+
"generate SQL" preview mode that never executes, a schema editor, and
|
|
15
|
+
multi-database support.
|
|
16
|
+
|
|
17
|
+
- **Backend:** Node.js + TypeScript (compiled to plain JS) on Express.
|
|
18
|
+
- **Frontend:** Handlebars server-rendered templates, Tailwind CSS + DaisyUI,
|
|
19
|
+
plain JavaScript (no frontend framework).
|
|
20
|
+
- **Database:** SQLite via [better-sqlite3](https://github.com/WiseLibs/better-sqlite3). Requires
|
|
21
|
+
**Node.js ≥ 20**.
|
|
22
|
+
|
|
23
|
+
> ## ⚠️ Security warning — read first
|
|
24
|
+
>
|
|
25
|
+
> **This tool exposes full, unauthenticated database access.** Every page and
|
|
26
|
+
> API route (browse, edit, delete, run arbitrary SQL, change the schema, export
|
|
27
|
+
> the whole database) is available to **anyone who can reach the server**.
|
|
28
|
+
>
|
|
29
|
+
> - **No authentication or authorization is built in.** The routes are **not
|
|
30
|
+
> protected**.
|
|
31
|
+
> - **It is your responsibility to protect access.** Do **not** expose
|
|
32
|
+
> AdminDB to the public internet or to untrusted networks.
|
|
33
|
+
> - Recommended ways to protect it:
|
|
34
|
+
> - bind the standalone server to `127.0.0.1` (`HOST=127.0.0.1`) and use it
|
|
35
|
+
> only from your own machine, and/or
|
|
36
|
+
> - run it behind a reverse proxy that requires authentication (Basic auth,
|
|
37
|
+
> OAuth, mTLS, …) or inside a VPN / private network.
|
|
38
|
+
>
|
|
39
|
+
> Treat AdminDB as if it were a remote `sqlite3` shell with write access.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install admindb
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires **Node.js ≥ 20**.
|
|
50
|
+
|
|
51
|
+
## Using as an npm package
|
|
52
|
+
|
|
53
|
+
AdminDB is an Express app you can mount inside your own application, under
|
|
54
|
+
your own path, on the same port as the rest of your server.
|
|
55
|
+
|
|
56
|
+
### Minimal example
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import express from 'express';
|
|
60
|
+
import { createRouter } from 'admindb';
|
|
61
|
+
|
|
62
|
+
const app = express();
|
|
63
|
+
|
|
64
|
+
app.get('/', (_req, res) => res.send('My main app'));
|
|
65
|
+
|
|
66
|
+
// All AdminDB routes live under /admin on the same port.
|
|
67
|
+
app.use('/admin', createRouter({ dbPath: '/data/my.db', basePath: '/admin' }));
|
|
68
|
+
|
|
69
|
+
app.listen(3000);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`createRouter(options)` returns a fully wired Express app (pages + JSON API +
|
|
73
|
+
static assets + view engine). Mounting it is just `app.use('/path', router)`.
|
|
74
|
+
|
|
75
|
+
### Options
|
|
76
|
+
|
|
77
|
+
| Option | Type | Description |
|
|
78
|
+
| ---------- | -------------------- | -------------------------------------------------------------- |
|
|
79
|
+
| `dbPath` | `string` | Path to a single SQLite file (single-db mode). Default: `admindb.db` |
|
|
80
|
+
| `db` | `SqliteDatabase` | An already-open database instance (advanced embedding) |
|
|
81
|
+
| `manager` | `DbManager` | Enables multi-database mode (see below) |
|
|
82
|
+
| `basePath` | `string` | URL prefix used by templates/assets (e.g. `/admin`). Pass the same prefix you mount at |
|
|
83
|
+
| `logger` | `Logger` | Custom logger (see `createLogger`) |
|
|
84
|
+
| `logLevel` | `'debug'\|'info'\|'warn'\|'error'` | Log verbosity (used when no logger is passed) |
|
|
85
|
+
| `readonly` | `boolean` | Open the database(s) **read-only**: every write is rejected (403), write controls are disabled in the UI, and a banner is shown. The DB file is opened with `SQLITE_OPEN_READONLY` + `PRAGMA query_only` as a belt-and-suspenders guard. Default: `false` |
|
|
86
|
+
|
|
87
|
+
Example with a custom logger and prefix:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import express from 'express';
|
|
91
|
+
import { createRouter, createLogger } from 'admindb';
|
|
92
|
+
|
|
93
|
+
const app = express();
|
|
94
|
+
app.use('/tools/db', createRouter({
|
|
95
|
+
dbPath: './data/app.db',
|
|
96
|
+
basePath: '/tools/db',
|
|
97
|
+
logLevel: 'info',
|
|
98
|
+
}));
|
|
99
|
+
app.listen(3000);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Multiple databases
|
|
103
|
+
|
|
104
|
+
Pass a `DbManager` to manage several database files — either from a directory,
|
|
105
|
+
from an explicit list of file paths, or both:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import express from 'express';
|
|
109
|
+
import { createRouter, DbManager, createLogger } from 'admindb';
|
|
110
|
+
|
|
111
|
+
const app = express();
|
|
112
|
+
app.use('/admin', createRouter({
|
|
113
|
+
manager: new DbManager(
|
|
114
|
+
{
|
|
115
|
+
dir: './data', // scan a directory
|
|
116
|
+
files: ['/srv/legacy/app.db', './shared.sqlite'], // and/or explicit paths
|
|
117
|
+
readonly: true, // open all databases read-only
|
|
118
|
+
},
|
|
119
|
+
createLogger('info'),
|
|
120
|
+
),
|
|
121
|
+
basePath: '/admin',
|
|
122
|
+
}));
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
In multi-db mode:
|
|
126
|
+
|
|
127
|
+
- a **"Databases" landing page** lets you open, create, and delete database files,
|
|
128
|
+
- every database is scoped under its file name, e.g.
|
|
129
|
+
`/admin/app.db/tables/users` and `/admin/app.db/api/tables`,
|
|
130
|
+
- file names that collide across sources are deduped (`name__2.db`).
|
|
131
|
+
|
|
132
|
+
## Features
|
|
133
|
+
|
|
134
|
+
- Browse every table and its rows (paginated, with primary-key awareness).
|
|
135
|
+
- **Filter rows by column** — exact (`=value`), comparison (`>5`, `<=10`), prefix
|
|
136
|
+
(`pre*`) or substring (plain text) matching; filters survive sorting and
|
|
137
|
+
pagination.
|
|
138
|
+
- Insert, edit, and delete rows (full CRUD). FK columns become dropdowns.
|
|
139
|
+
- **Export** table rows or query results as **CSV or JSON**, and **import a CSV
|
|
140
|
+
file** (or pasted CSV) into a table.
|
|
141
|
+
- Create tables visually: name, type, primary key, not-null / unique,
|
|
142
|
+
default value, and foreign-key references.
|
|
143
|
+
- Write and run arbitrary SQL — SELECTs render as a table, `COUNT` queries show
|
|
144
|
+
a readable summary, write statements execute and report affected rows.
|
|
145
|
+
- Save named queries and reload them from a dropdown.
|
|
146
|
+
- Export the whole database as a downloadable SQL dump (`CREATE` + `INSERT`).
|
|
147
|
+
- **Get query / preview mode:** generate `CREATE` / `INSERT` / `UPDATE` SQL from
|
|
148
|
+
the UI without executing it.
|
|
149
|
+
- **Edit schema:** rename the table, add / rename / drop columns (with
|
|
150
|
+
relationship-safety checks), and drop tables.
|
|
151
|
+
- **Indexes:** create indexes (plain or unique, on one or many columns — pick
|
|
152
|
+
columns in order, with a live `CREATE INDEX` SQL preview) and drop them from
|
|
153
|
+
the schema editor; automatic SQLite indexes are protected.
|
|
154
|
+
- **Views:** create / drop `CREATE VIEW` definitions, preview the rows they
|
|
155
|
+
return, and inspect their SQL — from a dedicated Views page.
|
|
156
|
+
- **Triggers:** create / drop triggers with a structured form (timing, single
|
|
157
|
+
event, optional `WHEN`, body with a live `CREATE TRIGGER` SQL preview) and
|
|
158
|
+
inspect existing trigger SQL — from a dedicated Triggers page.
|
|
159
|
+
- **Read-only mode:** open the database(s) without write access — the file is
|
|
160
|
+
opened `SQLITE_OPEN_READONLY` + `query_only`, every write route returns `403`,
|
|
161
|
+
and the UI hides/disables all write controls and shows a banner.
|
|
162
|
+
- **Multiple databases:** directory scanning and/or explicit file lists, each
|
|
163
|
+
with its own workspace under `/{db}/…`.
|
|
164
|
+
|
|
165
|
+
## Screenshots
|
|
166
|
+
|
|
167
|
+
Home dashboard — stat cards and the table list.
|
|
168
|
+
|
|
169
|
+

|
|
170
|
+
|
|
171
|
+
Table browser — sortable columns, sticky header, and per-row actions.
|
|
172
|
+
|
|
173
|
+

|
|
174
|
+
|
|
175
|
+
Query editor — line-numbered editor with a results table.
|
|
176
|
+
|
|
177
|
+

|
|
178
|
+
|
|
179
|
+
Table designer — visual columns with a live SQL preview.
|
|
180
|
+
|
|
181
|
+

|
|
182
|
+
|
|
183
|
+
Insert / edit form — column defaults are pre-filled.
|
|
184
|
+
|
|
185
|
+

|
|
186
|
+
|
|
187
|
+
Schema editor — rename the table, add / rename / drop columns safely.
|
|
188
|
+
|
|
189
|
+

|
|
190
|
+
|
|
191
|
+
Databases landing page (multi-db mode) — manage multiple SQLite files.
|
|
192
|
+
|
|
193
|
+

|
|
194
|
+
|
|
195
|
+
## Run the built-in server
|
|
196
|
+
|
|
197
|
+
For convenience a standalone server is included. From a clone of the repo:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
npm install
|
|
201
|
+
npm run build
|
|
202
|
+
npm start # open http://localhost:3000
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Configuration via environment variables:
|
|
206
|
+
|
|
207
|
+
| Variable | Default | Description |
|
|
208
|
+
| ----------- | ------------------ | ------------------------------------ |
|
|
209
|
+
| `PORT` | `3000` | Port to listen on |
|
|
210
|
+
| `HOST` | `0.0.0.0` | Host / interface to bind |
|
|
211
|
+
| `DB_PATH` | `admindb.db` | Single SQLite database file |
|
|
212
|
+
| `DB_DIR` | — | Multi-db source 1: a directory of `.db`/`.sqlite` files |
|
|
213
|
+
| `DB_FILES` | — | Multi-db source 2: comma-separated explicit database file paths |
|
|
214
|
+
| `READONLY` | — | `1` / `true` / `yes` / `on` opens the database(s) read-only |
|
|
215
|
+
| `BASE_PATH` | `''` | URL prefix (e.g. `/admin`) |
|
|
216
|
+
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` |
|
|
217
|
+
|
|
218
|
+
Multi-db mode activates when `DB_DIR` and/or `DB_FILES` is set:
|
|
219
|
+
|
|
220
|
+
```powershell
|
|
221
|
+
$env:DB_DIR='./db'; npm start # PowerShell
|
|
222
|
+
# bash/zsh: DB_DIR=./db npm start
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## The JSON REST API (under `basePath`)
|
|
226
|
+
|
|
227
|
+
| Method | Path | Purpose |
|
|
228
|
+
| ------ | ---------------------------------------- | -------------------------------- |
|
|
229
|
+
| GET | `/api/tables` | List tables |
|
|
230
|
+
| GET | `/api/tables/:table/info` | Column + FK metadata, PK columns |
|
|
231
|
+
| GET | `/api/tables/:table/fk-options` | Values for FK dropdowns |
|
|
232
|
+
| GET | `/api/tables/:table/rows?page&limit&f` | Paginated rows (`f` = URL-encoded JSON filters, e.g. `{"age":">35"}`) |
|
|
233
|
+
| GET | `/api/tables/:table/row/:id` | Single row by (encoded) PK |
|
|
234
|
+
| POST | `/api/tables/:table/rows` | Insert row |
|
|
235
|
+
| POST | `/api/tables/:table/rows/generate` | Generate INSERT SQL (no execute) |
|
|
236
|
+
| POST | `/api/tables/:table/rows/import` | Import CSV (`{ csv }`, header row must match columns) |
|
|
237
|
+
| GET | `/api/tables/:table/export?format=` | Download all rows as `csv` or `json` |
|
|
238
|
+
| PUT | `/api/tables/:table/row/:id` | Update row |
|
|
239
|
+
| PUT | `/api/tables/:table/row/:id/generate` | Generate UPDATE SQL (no execute) |
|
|
240
|
+
| DELETE | `/api/tables/:table/row/:id` | Delete row |
|
|
241
|
+
| POST | `/api/tables` | Create table |
|
|
242
|
+
| POST | `/api/tables/generate` | Generate CREATE SQL (no execute) |
|
|
243
|
+
| GET | `/api/tables/:table/schema` | Full schema (constraints, indexes, FK refs) |
|
|
244
|
+
| POST | `/api/tables/:table/rename` | Rename the table |
|
|
245
|
+
| POST | `/api/tables/:table/columns` | Add a column |
|
|
246
|
+
| PUT | `/api/tables/:table/columns/:column` | Rename a column |
|
|
247
|
+
| DELETE | `/api/tables/:table/columns/:column` | Drop a column (safety-checked) |
|
|
248
|
+
| DELETE | `/api/tables/:table` | Drop the table (safety-checked) |
|
|
249
|
+
| POST | `/api/tables/:table/indexes` | Create an index (`{ name?, columns[], unique? }`) |
|
|
250
|
+
| DELETE | `/api/tables/:table/indexes/:index` | Drop an index (auto indexes refused) |
|
|
251
|
+
| GET | `/api/views` | List views |
|
|
252
|
+
| GET | `/api/views/:name/rows` | Preview a view's rows |
|
|
253
|
+
| POST | `/api/views/generate` | Generate CREATE VIEW SQL (no execute) |
|
|
254
|
+
| POST | `/api/views` | Create a view (`{ name, sql }`) |
|
|
255
|
+
| DELETE | `/api/views/:name` | Drop a view |
|
|
256
|
+
| GET | `/api/triggers` | List triggers |
|
|
257
|
+
| POST | `/api/triggers/generate` | Generate CREATE TRIGGER SQL (no execute) |
|
|
258
|
+
| POST | `/api/triggers` | Create a trigger (`{ name, table, timing, event, when?, body }`) |
|
|
259
|
+
| DELETE | `/api/triggers/:name` | Drop a trigger |
|
|
260
|
+
| POST | `/api/query` | Run arbitrary SQL |
|
|
261
|
+
| POST | `/api/query/export` | Run a SELECT and download as `csv`/`json` |
|
|
262
|
+
| GET | `/api/queries` | List saved queries |
|
|
263
|
+
| POST | `/api/queries` | Save a named query |
|
|
264
|
+
| DELETE | `/api/queries/:id` | Delete a saved query |
|
|
265
|
+
|
|
266
|
+
In multi-db mode, every route is scoped under the database, e.g.
|
|
267
|
+
`/api/app.db/tables`. Every API response uses the consistent shape
|
|
268
|
+
`{ success, data?, error? }`.
|
|
269
|
+
|
|
270
|
+
## Behaviour notes
|
|
271
|
+
|
|
272
|
+
- **Empty input = "not set".** Empty form fields are omitted so DB defaults
|
|
273
|
+
apply; `0` is a valid value and is never treated as empty.
|
|
274
|
+
- **Insert forms pre-fill defaults.** On the "new row" form, columns that have
|
|
275
|
+
a schema default are pre-filled (string/number/boolean literals and
|
|
276
|
+
`CURRENT_TIMESTAMP`-style defaults) so you can see and adjust them.
|
|
277
|
+
- **Row filters.** Each column's filter supports exact match (`=value`),
|
|
278
|
+
comparison (`>5`, `>=5`, `<5`, `<=5`, `!=value`), prefix (`pre*`) and
|
|
279
|
+
case-insensitive substring (plain text). Filters are carried in the URL and
|
|
280
|
+
survive sorting and pagination.
|
|
281
|
+
- **CSV import.** The first row must be a header whose names match existing
|
|
282
|
+
columns (unknown or duplicate names are rejected). Empty cells are treated as
|
|
283
|
+
"not set" so database defaults apply. The whole import runs in a single
|
|
284
|
+
transaction — a failed row rolls everything back.
|
|
285
|
+
- **Indexes.** Creating an index takes one or more columns and an optional
|
|
286
|
+
unique flag (the index name is optional too). Only explicitly created indexes
|
|
287
|
+
(`origin = 'c'`) can be dropped from the UI — automatic primary-key/unique
|
|
288
|
+
indexes are protected.
|
|
289
|
+
- **Schema editor.** Drops are refused when the column is a primary key, has a
|
|
290
|
+
UNIQUE constraint, is used by an index, is part of a foreign key, or is
|
|
291
|
+
referenced by another table's foreign key. Tables referenced by other tables
|
|
292
|
+
cannot be dropped. Internal (`_`-prefixed) tables cannot be renamed or dropped.
|
|
293
|
+
- **Identifiers are quoted and string values escaped** everywhere SQL is built,
|
|
294
|
+
so generated SQL is correct and safe.
|
|
295
|
+
- **`COUNT` queries** return a readable summary message instead of a table.
|
|
296
|
+
- **"Get query" never executes** — it only returns the generated SQL string.
|
|
297
|
+
- **Internal table** `_saved_queries` stores saved queries and is kept out of
|
|
298
|
+
user-facing FK pickers. Schema initialization is idempotent and safe to run
|
|
299
|
+
repeatedly.
|
|
300
|
+
- Composite primary keys are supported (values are URL-encoded and comma-joined
|
|
301
|
+
in the row endpoints).
|
|
302
|
+
|
|
303
|
+
## Development
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npm install
|
|
307
|
+
npm run build # TypeScript → dist + Tailwind CSS
|
|
308
|
+
npm run dev # tsx watch server + Tailwind watch
|
|
309
|
+
npm test # builds and runs the unit tests
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`npm test` compiles TypeScript to `dist/` and runs the `node:test` suite
|
|
313
|
+
covering the SQL generator (INSERT / UPDATE / CREATE output, type mapping,
|
|
314
|
+
quoting, rejection of unsupported types), the SQL classifier, and the database
|
|
315
|
+
manager.
|
|
316
|
+
|
|
317
|
+
## Publishing to npm
|
|
318
|
+
|
|
319
|
+
The package ships the compiled `dist/` (with type declarations), this `README`,
|
|
320
|
+
and the `LICENSE`. When you are ready to publish:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
npm run build # happens automatically via the prepack script
|
|
324
|
+
npm login
|
|
325
|
+
npm publish
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Update `name`/`version` in `package.json` to match your intended package name
|
|
329
|
+
and add an `author`/`repository` if desired.
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
[MIT](./LICENSE) — see the [LICENSE](LICENSE) file.
|