admindb 2.2.1 → 2.3.0

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.
Files changed (87) hide show
  1. package/README.md +368 -364
  2. package/dist/app.js +6 -9
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +18 -30
  5. package/dist/auth/crypto.js +11 -21
  6. package/dist/auth/middleware.js +9 -16
  7. package/dist/auth/routes.js +9 -15
  8. package/dist/cli/args.d.ts +1 -1
  9. package/dist/cli/args.js +15 -26
  10. package/dist/cli/config.js +6 -16
  11. package/dist/cli/runner.js +4 -7
  12. package/dist/data/chain.d.ts +1 -1
  13. package/dist/data/chain.js +40 -617
  14. package/dist/data/datasets.d.ts +1 -1
  15. package/dist/data/datasets.js +43 -1
  16. package/dist/data/detector.js +45 -21
  17. package/dist/data/engine.d.ts +1 -1
  18. package/dist/data/engine.js +483 -142
  19. package/dist/data/index.d.ts +1 -1
  20. package/dist/data/index.js +8 -0
  21. package/dist/data/prng.d.ts +1 -0
  22. package/dist/data/prng.js +73 -0
  23. package/dist/data/registry.d.ts +1 -0
  24. package/dist/data/registry.js +805 -0
  25. package/dist/data/schema-graph.d.ts +1 -0
  26. package/dist/data/schema-graph.js +210 -0
  27. package/dist/data/strategies.d.ts +1 -1
  28. package/dist/data/strategies.js +44 -283
  29. package/dist/data/templates.d.ts +1 -0
  30. package/dist/data/templates.js +134 -0
  31. package/dist/data/types.d.ts +1 -1
  32. package/dist/data/validator.d.ts +1 -0
  33. package/dist/data/validator.js +160 -0
  34. package/dist/db/database.js +9 -14
  35. package/dist/db/export.js +32 -55
  36. package/dist/db/filters.d.ts +1 -1
  37. package/dist/db/filters.js +15 -38
  38. package/dist/db/introspection.js +4 -7
  39. package/dist/db/manager.js +27 -51
  40. package/dist/db/migrations.js +7 -14
  41. package/dist/db/postgres.js +30 -78
  42. package/dist/index.d.ts +1 -1
  43. package/dist/public/css/app.css +1 -1
  44. package/dist/public/js/browse.js +1 -1
  45. package/dist/public/js/inspector.js +1 -1
  46. package/dist/public/js/query.js +1 -1
  47. package/dist/public/js/seed.js +1 -1
  48. package/dist/routes/api/erd.js +3 -8
  49. package/dist/routes/api/helpers.d.ts +1 -1
  50. package/dist/routes/api/helpers.js +75 -25
  51. package/dist/routes/api/import-export.js +1 -2
  52. package/dist/routes/api/query.js +2 -6
  53. package/dist/routes/api/rows.js +4 -8
  54. package/dist/routes/api/seed.js +35 -21
  55. package/dist/routes/api/tables.js +15 -30
  56. package/dist/routes/databases.js +8 -26
  57. package/dist/routes/pages.js +67 -18
  58. package/dist/serverless.js +15 -40
  59. package/dist/sql/generator.d.ts +1 -1
  60. package/dist/sql/generator.js +35 -109
  61. package/dist/types/api.d.ts +1 -1
  62. package/dist/utils/common.d.ts +1 -1
  63. package/dist/utils/common.js +45 -85
  64. package/dist/utils/csv.d.ts +1 -1
  65. package/dist/utils/csv.js +11 -17
  66. package/dist/utils/datatype.js +46 -60
  67. package/dist/utils/icons.js +8 -8
  68. package/dist/views/layouts/main.hbs +1 -87
  69. package/dist/views/pages/databases.hbs +1 -233
  70. package/dist/views/pages/designer.hbs +1 -89
  71. package/dist/views/pages/erd.hbs +1 -1241
  72. package/dist/views/pages/error.hbs +1 -10
  73. package/dist/views/pages/form.hbs +1 -59
  74. package/dist/views/pages/home.hbs +1 -220
  75. package/dist/views/pages/info.hbs +1 -107
  76. package/dist/views/pages/login.hbs +1 -98
  77. package/dist/views/pages/query.hbs +1 -129
  78. package/dist/views/pages/schema.hbs +1 -298
  79. package/dist/views/pages/seed-select.hbs +251 -0
  80. package/dist/views/pages/seed.hbs +256 -414
  81. package/dist/views/pages/table.hbs +1 -327
  82. package/dist/views/partials/navbar.hbs +1 -83
  83. package/dist/views/partials/sidebar.hbs +1 -129
  84. package/docs/API.md +149 -149
  85. package/docs/EXAMPLES.md +579 -579
  86. package/docs/SECURITY.md +265 -265
  87. package/package.json +79 -79
@@ -40,12 +40,11 @@ function registerDatabasesRoutes(router, ctx) {
40
40
  };
41
41
  router.get('/', async (_req, res, next) => {
42
42
  try {
43
- const databases = await getDatabaseRows(manager);
44
43
  res.render('pages/databases', {
45
44
  title: 'Databases',
46
- databases,
45
+ databases: await getDatabaseRows(manager),
47
46
  dbDir: manager.directory,
48
- readonly: !!ctx.readonly,
47
+ readonly: Boolean(ctx.readonly),
49
48
  allowBrowse,
50
49
  browseRoot,
51
50
  config: { basePath: ctx.basePath },
@@ -56,8 +55,7 @@ function registerDatabasesRoutes(router, ctx) {
56
55
  }
57
56
  });
58
57
  router.get('/api/databases', wrap(async (_req, res) => {
59
- const data = await getDatabaseRows(manager);
60
- res.json({ success: true, data });
58
+ res.json({ success: true, data: await getDatabaseRows(manager) });
61
59
  }));
62
60
  const BLOCKED_NAMES = new Set([
63
61
  'node_modules',
@@ -73,9 +71,7 @@ function registerDatabasesRoutes(router, ctx) {
73
71
  function listDirectory(abs) {
74
72
  const entries = [];
75
73
  for (const name of (0, node_fs_1.readdirSync)(abs)) {
76
- if (name.startsWith('.'))
77
- continue;
78
- if (BLOCKED_NAMES.has(name) || name.toLowerCase().startsWith('.env'))
74
+ if (name.startsWith('.') || BLOCKED_NAMES.has(name) || name.toLowerCase().startsWith('.env'))
79
75
  continue;
80
76
  const full = node_path_1.default.join(abs, name);
81
77
  let isDir = false;
@@ -102,9 +98,8 @@ function registerDatabasesRoutes(router, ctx) {
102
98
  return res.status(403).json({ success: false, error: 'File browsing is disabled — this server was started with specific database files.' });
103
99
  }
104
100
  const raw = String(req.query.path ?? '').trim();
105
- if (raw.includes('\0')) {
101
+ if (raw.includes('\0'))
106
102
  return res.status(400).json({ success: false, error: 'Invalid path.' });
107
- }
108
103
  const base = raw ? node_path_1.default.resolve(raw) : manager.directory;
109
104
  if (browseRoot && !(0, common_1.isPathWithinRoot)(browseRoot, base)) {
110
105
  return res.status(403).json({ success: false, error: `Path is outside the allowed folder: ${browseRoot}` });
@@ -144,13 +139,7 @@ function registerDatabasesRoutes(router, ctx) {
144
139
  return res.status(403).json({ success: false, error: `Cannot open a database outside the allowed folder: ${browseRoot}` });
145
140
  }
146
141
  const isRo = req.body?.readonly !== undefined ? Boolean(req.body.readonly) : undefined;
147
- let id;
148
- try {
149
- id = manager.openFile(p, isRo);
150
- }
151
- catch (err) {
152
- return res.status(400).json({ success: false, error: (0, common_1.errorMessage)(err) });
153
- }
142
+ const id = manager.openFile(p, isRo);
154
143
  res.json({ success: true, data: { id, message: `Opened "${id}".` } });
155
144
  }));
156
145
  router.post('/api/databases/connect-postgres', wrap(async (req, res) => {
@@ -172,9 +161,8 @@ function registerDatabasesRoutes(router, ctx) {
172
161
  await db.listTables();
173
162
  }
174
163
  catch (err) {
175
- if (id) {
164
+ if (id)
176
165
  manager.remove(id);
177
- }
178
166
  return res.status(400).json({ success: false, error: `Failed to connect to PostgreSQL: ${(0, common_1.errorMessage)(err)}` });
179
167
  }
180
168
  res.status(201).json({ success: true, data: { id, message: `Connected to PostgreSQL database "${id}".` } });
@@ -205,13 +193,7 @@ function registerDatabasesRoutes(router, ctx) {
205
193
  const name = String(req.body?.name ?? '').trim();
206
194
  if (!name)
207
195
  return res.status(400).json({ success: false, error: 'Database name is required.' });
208
- let id;
209
- try {
210
- id = manager.create(name);
211
- }
212
- catch (err) {
213
- return res.status(400).json({ success: false, error: (0, common_1.errorMessage)(err) });
214
- }
196
+ const id = manager.create(name);
215
197
  res.status(201).json({ success: true, data: { id, message: `Database "${id}" created.` } });
216
198
  }));
217
199
  router.delete('/api/databases/:id', wrap(async (req, res) => {
@@ -21,6 +21,9 @@ function buildDisplayRows(rawRows, info, table, basePath = '') {
21
21
  let isImage = false;
22
22
  let isUrl = false;
23
23
  let isColor = false;
24
+ let isAudio = false;
25
+ let isVideo = false;
26
+ let isPdf = false;
24
27
  let blobSize = '';
25
28
  let blobMime = '';
26
29
  let blobExt = '';
@@ -32,10 +35,7 @@ function buildDisplayRows(rawRows, info, table, basePath = '') {
32
35
  if (Buffer.isBuffer(raw) || raw instanceof Uint8Array) {
33
36
  buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
34
37
  }
35
- else if (typeof v === 'string' && /^0x[0-9a-f]*$/i.test(v)) {
36
- buf = Buffer.from(v.slice(2), 'hex');
37
- }
38
- else if (typeof v === 'string' && /^\\x[0-9a-f]*$/i.test(v)) {
38
+ else if (typeof v === 'string' && (/^0x[0-9a-f]*$/i.test(v) || /^\\x[0-9a-f]*$/i.test(v))) {
39
39
  buf = Buffer.from(v.slice(2), 'hex');
40
40
  }
41
41
  else {
@@ -46,6 +46,9 @@ function buildDisplayRows(rawRows, info, table, basePath = '') {
46
46
  blobMime = mimeInfo.mime;
47
47
  blobExt = mimeInfo.ext;
48
48
  isImage = mimeInfo.isImage;
49
+ isAudio = mimeInfo.isAudio;
50
+ isVideo = mimeInfo.isVideo;
51
+ isPdf = mimeInfo.isPdf;
49
52
  if (table && pkEncoded) {
50
53
  blobUrl = `${basePath}/api/tables/${encodeURIComponent(table)}/row/${encodeURIComponent(pkEncoded)}/blob/${encodeURIComponent(c.name)}`;
51
54
  }
@@ -54,12 +57,10 @@ function buildDisplayRows(rawRows, info, table, basePath = '') {
54
57
  isJson = (0, datatype_1.isJsonString)(v);
55
58
  }
56
59
  else {
57
- if (/^https?:\/\/[^\s$.?#].[^\s]*$/i.test(str)) {
60
+ if (/^https?:\/\/[^\s$.?#].[^\s]*$/i.test(str))
58
61
  isUrl = true;
59
- }
60
- else if (/^#(?:[0-9a-fA-F]{3}){1,2}$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+/i.test(str)) {
62
+ else if (/^#(?:[0-9a-fA-F]{3}){1,2}$|^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+/i.test(str))
61
63
  isColor = true;
62
- }
63
64
  }
64
65
  }
65
66
  return {
@@ -71,6 +72,9 @@ function buildDisplayRows(rawRows, info, table, basePath = '') {
71
72
  isBlob,
72
73
  isJson,
73
74
  isImage,
75
+ isAudio,
76
+ isVideo,
77
+ isPdf,
74
78
  isUrl,
75
79
  isColor,
76
80
  blobSize,
@@ -92,7 +96,7 @@ function registerPages(router, ctx) {
92
96
  try {
93
97
  const names = res.locals.tables ?? [];
94
98
  const internalNames = res.locals.internalTables ?? [];
95
- let colCountsMap = {};
99
+ const colCountsMap = {};
96
100
  if (db.dialect === 'postgres') {
97
101
  const colRes = await db.all(`SELECT table_name, count(*)::int AS cols FROM information_schema.columns WHERE table_schema = 'public' GROUP BY table_name;`);
98
102
  if (colRes.success && colRes.data) {
@@ -173,15 +177,12 @@ function registerPages(router, ctx) {
173
177
  type: c.type,
174
178
  active,
175
179
  dir: active ? orderDir : null,
176
- href: `${basePath}/tables/${encodeURIComponent(table)}?${qs}${filterQS ? '&' + filterQS : ''}`,
180
+ href: `${basePath}/tables/${encodeURIComponent(table)}?${qs}${filterQS ? `&${filterQS}` : ''}`,
177
181
  };
178
182
  });
179
183
  const refColumns = (refsR.data ?? [])
180
184
  .filter((r) => !r.table.startsWith('_'))
181
- .map((rt) => {
182
- const ref = rt.refs[0];
183
- return { table: rt.table, from: ref?.from ?? '', to: ref?.to ?? '' };
184
- });
185
+ .map((rt) => ({ table: rt.table, from: rt.refs[0]?.from ?? '', to: rt.refs[0]?.to ?? '' }));
185
186
  const countEntries = await Promise.all(refColumns.map(async (col) => {
186
187
  const values = rawRows
187
188
  .map((r) => r[col.to])
@@ -271,9 +272,55 @@ function registerPages(router, ctx) {
271
272
  next(err);
272
273
  }
273
274
  });
274
- router.get('/tables/:table/seed', async (req, res, next) => {
275
+ router.get('/seed', async (req, res, next) => {
276
+ try {
277
+ const tableNames = res.locals.tables ?? [];
278
+ if (tableNames.length === 0) {
279
+ return res.render('pages/seed-select', {
280
+ title: 'Seed Data Generator',
281
+ tables: [],
282
+ hasTables: false,
283
+ dialect: db.dialect,
284
+ });
285
+ }
286
+ const tableStats = await Promise.all(tableNames.map(async (name) => {
287
+ const [info, countRes] = await Promise.all([db.getTableInfo(name), db.getRowCount(name)]);
288
+ const cols = info.success && info.data ? info.data.columns.length : 0;
289
+ const fks = info.success && info.data ? info.data.foreignKeys.length : 0;
290
+ const rowCount = countRes.success ? countRes.data : 0;
291
+ return {
292
+ name,
293
+ cols,
294
+ fks,
295
+ rowCount,
296
+ };
297
+ }));
298
+ const defaultTable = req.query.table ? String(req.query.table) : tableStats[0]?.name;
299
+ const defaultMode = req.query.mode === 'chain' ? 'chain' : 'single';
300
+ res.render('pages/seed-select', {
301
+ title: 'Seed Data Generator · Choose Table & Mode',
302
+ tables: tableStats,
303
+ hasTables: true,
304
+ defaultTable,
305
+ defaultMode,
306
+ dialect: db.dialect,
307
+ });
308
+ }
309
+ catch (err) {
310
+ next(err);
311
+ }
312
+ });
313
+ router.get('/tables/:table/seed', (req, res) => {
314
+ const table = req.params.table;
315
+ const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
316
+ const bp = res.locals.basePath || '';
317
+ return res.redirect(301, `${bp}/seed/${encodeURIComponent(table)}${qs}`);
318
+ });
319
+ router.get('/seed/:table', async (req, res, next) => {
275
320
  try {
276
321
  const table = req.params.table;
322
+ const allTables = res.locals.tables ?? [];
323
+ const initialMode = req.query.mode === 'chain' ? 'chain' : 'single';
277
324
  const [info, countRes] = await Promise.all([db.getTableInfo(table), db.getRowCount(table)]);
278
325
  if (!info.success || !info.data || info.data.columns.length === 0) {
279
326
  return notFound(res, `Table "${table}" does not exist.`);
@@ -284,11 +331,14 @@ function registerPages(router, ctx) {
284
331
  res.render('pages/seed', {
285
332
  title: `Seed data · ${table}`,
286
333
  table,
334
+ allTables,
335
+ mode: initialMode,
336
+ isChainMode: initialMode === 'chain',
287
337
  rowCount,
288
338
  colCount: info.data.columns.length,
289
339
  maxRows: index_1.MAX_SEED_ROWS,
290
340
  quickCounts: [10, 50, 100, 500, 1000, index_1.MAX_SEED_ROWS],
291
- seedConfig: { table, columns, rowCount, maxRows: index_1.MAX_SEED_ROWS },
341
+ seedConfig: { table, columns, rowCount, maxRows: index_1.MAX_SEED_ROWS, mode: initialMode },
292
342
  });
293
343
  }
294
344
  catch (err) {
@@ -382,10 +432,9 @@ function registerPages(router, ctx) {
382
432
  router.get('/info', async (_req, res, next) => {
383
433
  try {
384
434
  const settingsRes = await db.getSettings();
385
- const settings = settingsRes.success ? settingsRes.data : {};
386
435
  res.render('pages/info', {
387
436
  title: 'Database Info',
388
- settings,
437
+ settings: settingsRes.success ? settingsRes.data : {},
389
438
  dialect: db.dialect,
390
439
  dbPath: db.path,
391
440
  });
@@ -11,32 +11,19 @@ function isServerlessEnvironment(env = process.env) {
11
11
  if (['1', 'true', 'yes', 'on'].includes(String(env.ADMINDB_SERVERLESS ?? env.SERVERLESS ?? env.IS_SERVERLESS ?? '').trim().toLowerCase())) {
12
12
  return true;
13
13
  }
14
- if (Boolean(env.VERCEL || env.VERCEL_ENV || env.NOW_REGION)) {
15
- return true;
16
- }
17
- if (Boolean(env.AWS_LAMBDA_FUNCTION_NAME || env.LAMBDA_TASK_ROOT || env.AWS_EXECUTION_ENV)) {
18
- return true;
19
- }
20
- if (Boolean(env.NETLIFY || env.NETLIFY_LOCAL)) {
21
- return true;
22
- }
23
- if (Boolean(env.FUNCTION_TARGET || env.FUNCTION_NAME || env.K_SERVICE)) {
24
- return true;
25
- }
26
- if (Boolean(env.FUNCTIONS_WORKER_RUNTIME || env.WEBSITE_INSTANCE_ID)) {
27
- return true;
28
- }
29
- if (Boolean(env.CF_PAGES)) {
30
- return true;
31
- }
32
- return false;
14
+ return Boolean(env.VERCEL || env.VERCEL_ENV || env.NOW_REGION ||
15
+ env.AWS_LAMBDA_FUNCTION_NAME || env.LAMBDA_TASK_ROOT || env.AWS_EXECUTION_ENV ||
16
+ env.NETLIFY || env.NETLIFY_LOCAL ||
17
+ env.FUNCTION_TARGET || env.FUNCTION_NAME || env.K_SERVICE ||
18
+ env.FUNCTIONS_WORKER_RUNTIME || env.WEBSITE_INSTANCE_ID ||
19
+ env.CF_PAGES);
33
20
  }
34
21
  function createServerlessHandler(options = {}) {
35
22
  const rawTarget = options.connection || options.dbPath || '';
36
23
  const isPg = Boolean(options.pgOptions) ||
37
24
  (0, common_1.isPostgresConnectionString)(rawTarget) ||
38
25
  options.db?.dialect === 'postgres';
39
- const defaultReadonly = isPg ? false : true;
26
+ const defaultReadonly = !isPg;
40
27
  const app = (0, app_1.createRouter)({
41
28
  ...options,
42
29
  serverless: options.serverless ?? true,
@@ -51,7 +38,7 @@ function createLambdaHandler(options = {}) {
51
38
  const isPg = Boolean(options.pgOptions) ||
52
39
  (0, common_1.isPostgresConnectionString)(rawTarget) ||
53
40
  options.db?.dialect === 'postgres';
54
- const defaultReadonly = isPg ? false : true;
41
+ const defaultReadonly = !isPg;
55
42
  const app = (0, app_1.createRouter)({
56
43
  ...options,
57
44
  serverless: options.serverless ?? true,
@@ -61,9 +48,7 @@ function createLambdaHandler(options = {}) {
61
48
  return new Promise((resolve) => {
62
49
  try {
63
50
  const isV2 = Boolean(event.version === '2.0' || event.rawPath);
64
- const method = (isV2
65
- ? event.requestContext?.http?.method
66
- : event.httpMethod) || 'GET';
51
+ const method = (isV2 ? event.requestContext?.http?.method : event.httpMethod) || 'GET';
67
52
  const rawPath = isV2 ? (event.rawPath || '/') : (event.path || '/');
68
53
  const qs = isV2
69
54
  ? (event.rawQueryString || '')
@@ -76,24 +61,17 @@ function createLambdaHandler(options = {}) {
76
61
  const rawHeaders = event.headers || {};
77
62
  const headers = {};
78
63
  for (const [k, v] of Object.entries(rawHeaders)) {
79
- if (v !== undefined && v !== null) {
64
+ if (v !== undefined && v !== null)
80
65
  headers[k.toLowerCase()] = String(v);
81
- }
82
66
  }
83
67
  if (event.cookies && Array.isArray(event.cookies) && !headers['cookie']) {
84
68
  headers['cookie'] = event.cookies.join('; ');
85
69
  }
86
70
  let bodyBuffer = null;
87
71
  if (event.body) {
88
- if (event.isBase64Encoded) {
89
- bodyBuffer = Buffer.from(event.body, 'base64');
90
- }
91
- else {
92
- bodyBuffer = Buffer.from(event.body, 'utf8');
93
- }
94
- if (!headers['content-length']) {
72
+ bodyBuffer = event.isBase64Encoded ? Buffer.from(event.body, 'base64') : Buffer.from(event.body, 'utf8');
73
+ if (!headers['content-length'])
95
74
  headers['content-length'] = String(bodyBuffer.length);
96
- }
97
75
  }
98
76
  const socket = new node_net_1.Socket();
99
77
  const req = new node_http_1.IncomingMessage(socket);
@@ -103,16 +81,14 @@ function createLambdaHandler(options = {}) {
103
81
  req.httpVersion = '1.1';
104
82
  req.httpVersionMajor = 1;
105
83
  req.httpVersionMinor = 1;
106
- if (bodyBuffer) {
84
+ if (bodyBuffer)
107
85
  req.push(bodyBuffer);
108
- }
109
86
  req.push(null);
110
87
  const res = new node_http_1.ServerResponse(req);
111
88
  const chunks = [];
112
89
  res.write = function (chunk, ...args) {
113
- if (chunk) {
90
+ if (chunk)
114
91
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
115
- }
116
92
  const cb = typeof args[0] === 'function' ? args[0] : typeof args[1] === 'function' ? args[1] : undefined;
117
93
  if (cb)
118
94
  cb();
@@ -133,8 +109,7 @@ function createLambdaHandler(options = {}) {
133
109
  cb();
134
110
  const responseHeaders = {};
135
111
  const multiValueHeaders = {};
136
- const resHeaders = res.getHeaders();
137
- for (const [k, v] of Object.entries(resHeaders)) {
112
+ for (const [k, v] of Object.entries(res.getHeaders())) {
138
113
  if (Array.isArray(v)) {
139
114
  multiValueHeaders[k] = v.map(String);
140
115
  responseHeaders[k] = v.map(String).join(', ');
@@ -1 +1 @@
1
- export declare const SQLITE_DESIGNER_TYPES:readonly ["TEXT","INTEGER","REAL","BLOB","BOOLEAN","DATE","DATETIME","NUMERIC","VARCHAR(255)","JSON"];export declare const POSTGRES_DESIGNER_TYPES:readonly ["TEXT","VARCHAR(255)","INTEGER","BIGINT","SMALLINT","SERIAL","BIGSERIAL","REAL","DECIMAL(10,2)","NUMERIC","BOOLEAN","DATE","DATETIME","TIMESTAMP","TIMESTAMPTZ","TIME","INTERVAL","JSON","JSONB","UUID","BYTEA","INET"];export declare const DESIGNER_TYPES:readonly ["TEXT","VARCHAR(255)","INTEGER","BIGINT","SMALLINT","SERIAL","BIGSERIAL","REAL","DECIMAL(10,2)","NUMERIC","BOOLEAN","DATE","DATETIME","TIMESTAMP","TIMESTAMPTZ","TIME","INTERVAL","JSON","JSONB","UUID","BYTEA","BLOB","INET"];export declare function getDesignerTypes(dialect?:'sqlite'|'postgres'):readonly string[];export type DesignerColumnType=( typeof DESIGNER_TYPES)[number];export interface ColumnDef{name:string;type:string;primaryKey?:boolean;notNull?:boolean;unique?:boolean;defaultValue?:string|null;foreignKey?:{table:string;column:string;}|null;}export interface FieldValue{column:string;value:unknown;}export declare function quoteIdentifier(identifier:string):string;export declare function escapeString(value:string):string;export declare function sqlValue(value:unknown):string;export interface MappedType{sqlType:string;defaultAuto?:string;}export declare function mapColumnType(type:string):MappedType;export interface RenderOptions{forAddColumn?:boolean;}export declare function formatSqlDefault(raw:string,colType?:string):string;export declare function renderColumnDef(col:ColumnDef,opts?:RenderOptions):string;export declare function generateCreateTable(tableName:string,columns:ColumnDef[]):string;export declare function generateAddColumn(tableName:string,col:ColumnDef):string;export declare function generateRenameTable(tableName:string,newName:string):string;export declare function generateRenameColumn(tableName:string,oldName:string,newName:string):string;export declare function generateDropColumn(tableName:string,column:string):string;export declare function generateDropTable(tableName:string):string;export interface IndexDef{name?:string;columns:string[];unique?:boolean;}export declare function generateCreateIndex(tableName:string,index:IndexDef):string;export declare function generateDropIndex(indexName:string):string;export declare function generateInsert(table:string,fields:FieldValue[]):string;export declare function generateUpdate(table:string,fields:FieldValue[],where:FieldValue[]):string;
1
+ export declare const SQLITE_DESIGNER_TYPES:readonly ["TEXT","INTEGER","REAL","BLOB","BOOLEAN","DATE","DATETIME","NUMERIC","VARCHAR(255)","JSON"];export declare const POSTGRES_DESIGNER_TYPES:readonly ["TEXT","VARCHAR(255)","INTEGER","BIGINT","SMALLINT","SERIAL","BIGSERIAL","REAL","DECIMAL(10,2)","NUMERIC","BOOLEAN","DATE","DATETIME","TIMESTAMP","TIMESTAMPTZ","TIME","INTERVAL","JSON","JSONB","UUID","BYTEA","INET"];export declare const DESIGNER_TYPES:readonly ["TEXT","VARCHAR(255)","INTEGER","BIGINT","SMALLINT","SERIAL","BIGSERIAL","REAL","DECIMAL(10,2)","NUMERIC","BOOLEAN","DATE","DATETIME","TIMESTAMP","TIMESTAMPTZ","TIME","INTERVAL","JSON","JSONB","UUID","BYTEA","BLOB","INET"];export declare const getDesignerTypes:(dialect?:"sqlite"|"postgres")=>readonly string[];export type DesignerColumnType=( typeof DESIGNER_TYPES)[number];export interface ColumnDef{name:string;type:string;primaryKey?:boolean;notNull?:boolean;unique?:boolean;defaultValue?:string|null;foreignKey?:{table:string;column:string;}|null;}export interface FieldValue{column:string;value:unknown;}export declare const quoteIdentifier:(identifier:string)=>string;export declare const escapeString:(value:string)=>string;export declare function sqlValue(value:unknown):string;export interface MappedType{sqlType:string;defaultAuto?:string;}export declare function mapColumnType(type:string):MappedType;export interface RenderOptions{forAddColumn?:boolean;}export declare function formatSqlDefault(raw:string,colType?:string):string;export declare function renderColumnDef(col:ColumnDef,opts?:RenderOptions):string;export declare function generateCreateTable(tableName:string,columns:ColumnDef[]):string;export declare function generateAddColumn(tableName:string,col:ColumnDef):string;export declare function generateRenameTable(tableName:string,newName:string):string;export declare function generateRenameColumn(tableName:string,oldName:string,newName:string):string;export declare function generateDropColumn(tableName:string,column:string):string;export declare function generateDropTable(tableName:string):string;export interface IndexDef{name?:string;columns:string[];unique?:boolean;}export declare function generateCreateIndex(tableName:string,index:IndexDef):string;export declare function generateDropIndex(indexName:string):string;export declare function generateInsert(table:string,fields:FieldValue[]):string;export declare function generateUpdate(table:string,fields:FieldValue[],where:FieldValue[]):string;
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DESIGNER_TYPES = exports.POSTGRES_DESIGNER_TYPES = exports.SQLITE_DESIGNER_TYPES = void 0;
4
- exports.getDesignerTypes = getDesignerTypes;
5
- exports.quoteIdentifier = quoteIdentifier;
6
- exports.escapeString = escapeString;
3
+ exports.escapeString = exports.quoteIdentifier = exports.getDesignerTypes = exports.DESIGNER_TYPES = exports.POSTGRES_DESIGNER_TYPES = exports.SQLITE_DESIGNER_TYPES = void 0;
7
4
  exports.sqlValue = sqlValue;
8
5
  exports.mapColumnType = mapColumnType;
9
6
  exports.formatSqlDefault = formatSqlDefault;
@@ -18,79 +15,18 @@ exports.generateCreateIndex = generateCreateIndex;
18
15
  exports.generateDropIndex = generateDropIndex;
19
16
  exports.generateInsert = generateInsert;
20
17
  exports.generateUpdate = generateUpdate;
21
- exports.SQLITE_DESIGNER_TYPES = [
22
- 'TEXT',
23
- 'INTEGER',
24
- 'REAL',
25
- 'BLOB',
26
- 'BOOLEAN',
27
- 'DATE',
28
- 'DATETIME',
29
- 'NUMERIC',
30
- 'VARCHAR(255)',
31
- 'JSON',
32
- ];
33
- exports.POSTGRES_DESIGNER_TYPES = [
34
- 'TEXT',
35
- 'VARCHAR(255)',
36
- 'INTEGER',
37
- 'BIGINT',
38
- 'SMALLINT',
39
- 'SERIAL',
40
- 'BIGSERIAL',
41
- 'REAL',
42
- 'DECIMAL(10,2)',
43
- 'NUMERIC',
44
- 'BOOLEAN',
45
- 'DATE',
46
- 'DATETIME',
47
- 'TIMESTAMP',
48
- 'TIMESTAMPTZ',
49
- 'TIME',
50
- 'INTERVAL',
51
- 'JSON',
52
- 'JSONB',
53
- 'UUID',
54
- 'BYTEA',
55
- 'INET',
56
- ];
57
- exports.DESIGNER_TYPES = [
58
- 'TEXT',
59
- 'VARCHAR(255)',
60
- 'INTEGER',
61
- 'BIGINT',
62
- 'SMALLINT',
63
- 'SERIAL',
64
- 'BIGSERIAL',
65
- 'REAL',
66
- 'DECIMAL(10,2)',
67
- 'NUMERIC',
68
- 'BOOLEAN',
69
- 'DATE',
70
- 'DATETIME',
71
- 'TIMESTAMP',
72
- 'TIMESTAMPTZ',
73
- 'TIME',
74
- 'INTERVAL',
75
- 'JSON',
76
- 'JSONB',
77
- 'UUID',
78
- 'BYTEA',
79
- 'BLOB',
80
- 'INET',
81
- ];
82
- function getDesignerTypes(dialect = 'sqlite') {
83
- return dialect === 'postgres' ? exports.POSTGRES_DESIGNER_TYPES : exports.SQLITE_DESIGNER_TYPES;
84
- }
18
+ exports.SQLITE_DESIGNER_TYPES = ['TEXT', 'INTEGER', 'REAL', 'BLOB', 'BOOLEAN', 'DATE', 'DATETIME', 'NUMERIC', 'VARCHAR(255)', 'JSON',];
19
+ exports.POSTGRES_DESIGNER_TYPES = ['TEXT', 'VARCHAR(255)', 'INTEGER', 'BIGINT', 'SMALLINT', 'SERIAL', 'BIGSERIAL', 'REAL', 'DECIMAL(10,2)', 'NUMERIC', 'BOOLEAN', 'DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMPTZ', 'TIME', 'INTERVAL', 'JSON', 'JSONB', 'UUID', 'BYTEA', 'INET',];
20
+ exports.DESIGNER_TYPES = ['TEXT', 'VARCHAR(255)', 'INTEGER', 'BIGINT', 'SMALLINT', 'SERIAL', 'BIGSERIAL', 'REAL', 'DECIMAL(10,2)', 'NUMERIC', 'BOOLEAN', 'DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMPTZ', 'TIME', 'INTERVAL', 'JSON', 'JSONB', 'UUID', 'BYTEA', 'BLOB', 'INET',];
21
+ const getDesignerTypes = (dialect = 'sqlite') => dialect === 'postgres' ? exports.POSTGRES_DESIGNER_TYPES : exports.SQLITE_DESIGNER_TYPES;
22
+ exports.getDesignerTypes = getDesignerTypes;
85
23
  const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_$]*$/;
86
- function quoteIdentifier(identifier) {
87
- return `"${String(identifier).replace(/"/g, '""')}"`;
88
- }
89
- function escapeString(value) {
90
- return `'${String(value).replace(/'/g, "''")}'`;
91
- }
24
+ const quoteIdentifier = (identifier) => `"${String(identifier).replace(/"/g, '""')}"`;
25
+ exports.quoteIdentifier = quoteIdentifier;
26
+ const escapeString = (value) => `'${String(value).replace(/'/g, "''")}'`;
27
+ exports.escapeString = escapeString;
92
28
  function sqlValue(value) {
93
- if (value === null || value === undefined)
29
+ if (value == null)
94
30
  return 'NULL';
95
31
  if (typeof value === 'number')
96
32
  return Number.isFinite(value) ? String(value) : 'NULL';
@@ -101,8 +37,8 @@ function sqlValue(value) {
101
37
  if (value instanceof Uint8Array)
102
38
  return `X'${Buffer.from(value).toString('hex')}'`;
103
39
  if (value instanceof Date)
104
- return escapeString(value.toISOString());
105
- return escapeString(String(value));
40
+ return (0, exports.escapeString)(value.toISOString());
41
+ return (0, exports.escapeString)(String(value));
106
42
  }
107
43
  function mapColumnType(type) {
108
44
  const t = String(type ?? '').trim().toUpperCase();
@@ -151,12 +87,10 @@ function mapColumnType(type) {
151
87
  case 'TIMESTAMPTZ':
152
88
  case 'TIMESTAMP WITHOUT TIME ZONE':
153
89
  case 'TIMESTAMP WITH TIME ZONE':
154
- return { sqlType: t };
155
90
  case 'TIME':
156
91
  case 'TIMETZ':
157
92
  case 'TIME WITHOUT TIME ZONE':
158
93
  case 'TIME WITH TIME ZONE':
159
- return { sqlType: t };
160
94
  case 'INTERVAL':
161
95
  case 'JSON':
162
96
  case 'JSONB':
@@ -164,14 +98,10 @@ function mapColumnType(type) {
164
98
  case 'INET':
165
99
  case 'CIDR':
166
100
  case 'MACADDR':
167
- case 'SERIAL':
168
- case 'BIGSERIAL':
169
- case 'SMALLSERIAL':
170
101
  return { sqlType: t };
171
102
  default:
172
- if (/^[A-Za-z0-9_(),\s\[\]]+$/.test(t)) {
103
+ if (/^[A-Za-z0-9_(),\s\[\]]+$/.test(t))
173
104
  return { sqlType: t };
174
- }
175
105
  throw new Error(`Unsupported column type: "${type}". Supported types include: ${exports.DESIGNER_TYPES.join(', ')} or standard SQL types.`);
176
106
  }
177
107
  }
@@ -194,15 +124,13 @@ function formatSqlDefault(raw, colType) {
194
124
  return s;
195
125
  if (/^(true|false)$/i.test(s))
196
126
  return s.toUpperCase() === 'TRUE' ? '1' : '0';
197
- if (/^'.*'$/s.test(s) || /^".*"$/s.test(s))
198
- return s;
199
- if (/^[a-zA-Z_]\w*\s*\(.*\)$/s.test(s))
127
+ if (/^'.*'$/s.test(s) || /^".*"$/s.test(s) || /^[a-zA-Z_]\w*\s*\(.*\)$/s.test(s))
200
128
  return s;
201
129
  if (/^CURRENT_TIMESTAMP|CURRENT_DATE|CURRENT_TIME$/i.test(s))
202
130
  return s.toUpperCase();
203
131
  const t = (colType || '').toUpperCase();
204
132
  if (t.includes('TEXT') || t.includes('CHAR') || t.includes('CLOB') || t.includes('VARCHAR') || t.includes('STRING')) {
205
- return escapeString(s);
133
+ return (0, exports.escapeString)(s);
206
134
  }
207
135
  return s;
208
136
  }
@@ -218,7 +146,7 @@ function renderColumnDef(col, opts = {}) {
218
146
  throw new Error(`Cannot add "${colName}" with UNIQUE: ALTER TABLE ADD COLUMN does not support UNIQUE constraints.`);
219
147
  }
220
148
  }
221
- let def = `${quoteIdentifier(colName)} ${mapped.sqlType}`;
149
+ let def = `${(0, exports.quoteIdentifier)(colName)} ${mapped.sqlType}`;
222
150
  if (!opts.forAddColumn && col.primaryKey)
223
151
  def += ' PRIMARY KEY';
224
152
  if (col.notNull)
@@ -227,8 +155,9 @@ function renderColumnDef(col, opts = {}) {
227
155
  def += ' UNIQUE';
228
156
  const rawDefault = formatSqlDefault(String(col.defaultValue ?? ''), col.type);
229
157
  const dflt = rawDefault || mapped.defaultAuto || '';
230
- if (dflt)
158
+ if (dflt) {
231
159
  def += ` DEFAULT ${dflt}`;
160
+ }
232
161
  else if (opts.forAddColumn && col.notNull) {
233
162
  throw new Error(`Cannot add "${colName}" as NOT NULL without a default value.`);
234
163
  }
@@ -236,7 +165,7 @@ function renderColumnDef(col, opts = {}) {
236
165
  if (opts.forAddColumn && (col.notNull || dflt)) {
237
166
  throw new Error(`Cannot add "${colName}" as a foreign key with NOT NULL or a non-NULL default.`);
238
167
  }
239
- def += ` REFERENCES ${quoteIdentifier(col.foreignKey.table)}(${quoteIdentifier(col.foreignKey.column)})`;
168
+ def += ` REFERENCES ${(0, exports.quoteIdentifier)(col.foreignKey.table)}(${(0, exports.quoteIdentifier)(col.foreignKey.column)})`;
240
169
  }
241
170
  return def;
242
171
  }
@@ -256,38 +185,37 @@ function generateCreateTable(tableName, columns) {
256
185
  throw new Error(`Duplicate column name: "${colName}".`);
257
186
  seen.add(colName);
258
187
  if (col.primaryKey)
259
- pkCount += 1;
188
+ pkCount++;
260
189
  parts.push(renderColumnDef(col));
261
190
  }
262
191
  if (pkCount > 1)
263
192
  throw new Error('At most one primary key column is allowed.');
264
- return `CREATE TABLE ${quoteIdentifier(table)} (\n ${parts.join(',\n ')}\n);`;
193
+ return `CREATE TABLE ${(0, exports.quoteIdentifier)(table)} (\n ${parts.join(',\n ')}\n);`;
265
194
  }
266
195
  function generateAddColumn(tableName, col) {
267
196
  const table = String(tableName ?? '').trim();
268
197
  validateIdentifier('table', table);
269
- const def = renderColumnDef(col, { forAddColumn: true });
270
- return `ALTER TABLE ${quoteIdentifier(table)} ADD COLUMN ${def};`;
198
+ return `ALTER TABLE ${(0, exports.quoteIdentifier)(table)} ADD COLUMN ${renderColumnDef(col, { forAddColumn: true })};`;
271
199
  }
272
200
  function generateRenameTable(tableName, newName) {
273
201
  validateIdentifier('table', tableName);
274
202
  validateIdentifier('table', newName);
275
- return `ALTER TABLE ${quoteIdentifier(tableName)} RENAME TO ${quoteIdentifier(newName)};`;
203
+ return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} RENAME TO ${(0, exports.quoteIdentifier)(newName)};`;
276
204
  }
277
205
  function generateRenameColumn(tableName, oldName, newName) {
278
206
  validateIdentifier('table', tableName);
279
207
  validateIdentifier('column', oldName);
280
208
  validateIdentifier('column', newName);
281
- return `ALTER TABLE ${quoteIdentifier(tableName)} RENAME COLUMN ${quoteIdentifier(oldName)} TO ${quoteIdentifier(newName)};`;
209
+ return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} RENAME COLUMN ${(0, exports.quoteIdentifier)(oldName)} TO ${(0, exports.quoteIdentifier)(newName)};`;
282
210
  }
283
211
  function generateDropColumn(tableName, column) {
284
212
  validateIdentifier('table', tableName);
285
213
  validateIdentifier('column', column);
286
- return `ALTER TABLE ${quoteIdentifier(tableName)} DROP COLUMN ${quoteIdentifier(column)};`;
214
+ return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} DROP COLUMN ${(0, exports.quoteIdentifier)(column)};`;
287
215
  }
288
216
  function generateDropTable(tableName) {
289
217
  validateIdentifier('table', tableName);
290
- return `DROP TABLE ${quoteIdentifier(tableName)};`;
218
+ return `DROP TABLE ${(0, exports.quoteIdentifier)(tableName)};`;
291
219
  }
292
220
  function generateCreateIndex(tableName, index) {
293
221
  validateIdentifier('table', tableName);
@@ -297,23 +225,21 @@ function generateCreateIndex(tableName, index) {
297
225
  for (const c of cols)
298
226
  validateIdentifier('column', c);
299
227
  const unique = index.unique ? 'UNIQUE ' : '';
300
- let name = String(index?.name ?? '').trim();
301
- if (!name)
302
- name = `idx_${tableName}_${cols.join('_')}`;
228
+ const name = String(index?.name ?? '').trim() || `idx_${tableName}_${cols.join('_')}`;
303
229
  validateIdentifier('index', name);
304
- return `CREATE ${unique}INDEX ${quoteIdentifier(name)} ON ${quoteIdentifier(tableName)} (${cols.map(quoteIdentifier).join(', ')});`;
230
+ return `CREATE ${unique}INDEX ${(0, exports.quoteIdentifier)(name)} ON ${(0, exports.quoteIdentifier)(tableName)} (${cols.map(exports.quoteIdentifier).join(', ')});`;
305
231
  }
306
232
  function generateDropIndex(indexName) {
307
233
  validateIdentifier('index', indexName);
308
- return `DROP INDEX ${quoteIdentifier(indexName)};`;
234
+ return `DROP INDEX ${(0, exports.quoteIdentifier)(indexName)};`;
309
235
  }
310
236
  function generateInsert(table, fields) {
311
- const cols = fields.map((f) => quoteIdentifier(f.column));
237
+ const cols = fields.map((f) => (0, exports.quoteIdentifier)(f.column));
312
238
  const vals = fields.map((f) => sqlValue(f.value));
313
- return `INSERT INTO ${quoteIdentifier(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')});`;
239
+ return `INSERT INTO ${(0, exports.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')});`;
314
240
  }
315
241
  function generateUpdate(table, fields, where) {
316
- const sets = fields.map((f) => `${quoteIdentifier(f.column)} = ${sqlValue(f.value)}`);
317
- const conds = where.map((w) => `${quoteIdentifier(w.column)} = ${sqlValue(w.value)}`);
318
- return `UPDATE ${quoteIdentifier(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')};`;
242
+ const sets = fields.map((f) => `${(0, exports.quoteIdentifier)(f.column)} = ${sqlValue(f.value)}`);
243
+ const conds = where.map((w) => `${(0, exports.quoteIdentifier)(w.column)} = ${sqlValue(w.value)}`);
244
+ return `UPDATE ${(0, exports.quoteIdentifier)(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')};`;
319
245
  }