@devmoods/postgres-admin 0.12.0 → 0.13.1

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/dist/index.d.ts CHANGED
@@ -4,13 +4,14 @@ import { createPostgres } from '@devmoods/express-extras';
4
4
  interface AdminRouterOptions {
5
5
  postgres?: ReturnType<typeof createPostgres>;
6
6
  basename?: string;
7
+ indexHtml?: string;
7
8
  uiOptions?: AdminUIOptions;
8
9
  }
9
10
  interface AdminUIOptions {
10
11
  title?: string;
11
12
  }
12
- declare function createRouter({ postgres: defaultPostgres, basename, uiOptions, }: AdminRouterOptions): express_serve_static_core.Router;
13
+ declare function createAdminRouter({ postgres: defaultPostgres, basename, indexHtml, uiOptions, }: AdminRouterOptions): express_serve_static_core.Router;
13
14
 
14
- declare const admin: typeof createRouter;
15
+ declare const admin: typeof createAdminRouter;
15
16
 
16
17
  export { admin };
package/dist/index.js CHANGED
@@ -6,11 +6,11 @@ import {
6
6
  createPostgres,
7
7
  generateCsrfToken,
8
8
  joinStatements,
9
- route,
10
9
  sql,
11
- validateQuery
10
+ staticMiddleware,
11
+ createRouter
12
12
  } from "@devmoods/express-extras";
13
- import express from "express";
13
+ import { type } from "arktype";
14
14
 
15
15
  // src/api/structure.ts
16
16
  import { NotFound, readEnv } from "@devmoods/express-extras";
@@ -85,13 +85,13 @@ function getDefault(value) {
85
85
  }
86
86
  function toJsonSchemaProperty(column) {
87
87
  const defaultValue = getDefault(column.default);
88
- const type = getPrimitiveType(column.type.name);
88
+ const type2 = getPrimitiveType(column.type.name);
89
89
  const description = column.comment;
90
90
  if (column.arrayDimension > 0) {
91
91
  return {
92
92
  type: "array",
93
93
  title: column.name,
94
- items: { type },
94
+ items: { type: type2 },
95
95
  default: defaultValue || [],
96
96
  description
97
97
  };
@@ -105,7 +105,7 @@ function toJsonSchemaProperty(column) {
105
105
  };
106
106
  }
107
107
  return {
108
- type,
108
+ type: type2,
109
109
  title: column.name,
110
110
  default: defaultValue,
111
111
  description,
@@ -225,74 +225,65 @@ function counter() {
225
225
  }
226
226
 
227
227
  // src/api/app.ts
228
- function createRouter({
228
+ function createAdminRouter({
229
229
  postgres: defaultPostgres,
230
230
  basename = "/admin",
231
+ indexHtml = "node_modules/@devmoods/postgres-admin/dist/client/_index.html",
231
232
  uiOptions = {}
232
233
  }) {
233
234
  const postgres = defaultPostgres ?? createPostgres();
234
- const app = express.Router();
235
+ const router = createRouter();
235
236
  if (!basename.startsWith("/")) {
236
237
  throw new Error(`Option 'basename' must start with a slash /`);
237
238
  }
238
- app.get(
239
- "/api/tables",
240
- route(async () => {
241
- const { databaseName, tables } = await readTables();
242
- const rowCounts = Object.fromEntries(
243
- await Promise.all(
244
- tables.map(async (table) => {
245
- return [
246
- table.tableName,
247
- await postgres.get(sql`SELECT COUNT(*) FROM ${sql.identifier(table.tableName)}`).then((r) => +r.count)
248
- ];
249
- })
250
- )
251
- );
252
- return {
253
- databaseName,
254
- tables,
255
- rowCounts
256
- };
257
- })
239
+ const unknownTableRow = type(
240
+ "Record<string, string | number | boolean | null>"
258
241
  );
259
- app.get(
260
- "/api/tables/:table/suggestions/:column",
261
- route(async (req) => {
262
- const table = stripIdentifier(req.params.table);
263
- const definition = await getTable(table);
264
- const column = stripIdentifier(req.params.column);
265
- if (!definition.columnNames.includes(column)) {
266
- throw BadRequest(
267
- `Invalid column ${column}. Must be one of: ${definition.columnNames.join(", ")}`
268
- );
269
- }
270
- const statement = sql`
242
+ router.get("/api/tables", {}, async () => {
243
+ const { databaseName, tables } = await readTables();
244
+ const rowCounts = Object.fromEntries(
245
+ await Promise.all(
246
+ tables.map(async (table) => {
247
+ return [
248
+ table.tableName,
249
+ await postgres.get(sql`SELECT COUNT(*) FROM ${sql.identifier(table.tableName)}`).then((r) => +r.count)
250
+ ];
251
+ })
252
+ )
253
+ );
254
+ return {
255
+ databaseName,
256
+ tables,
257
+ rowCounts
258
+ };
259
+ });
260
+ router.get("/api/tables/:table/suggestions/:column", {}, async (req) => {
261
+ const table = stripIdentifier(req.params.table);
262
+ const definition = await getTable(table);
263
+ const column = stripIdentifier(req.params.column);
264
+ if (!definition.columnNames.includes(column)) {
265
+ throw BadRequest(
266
+ `Invalid column ${column}. Must be one of: ${definition.columnNames.join(", ")}`
267
+ );
268
+ }
269
+ const statement = sql`
271
270
  SELECT DISTINCT ${sql.identifier(column)}, *
272
271
  FROM ${sql.identifier(table)}
273
272
  LIMIT 500
274
273
  `;
275
- return postgres.all(statement);
276
- })
277
- );
278
- app.get(
274
+ return postgres.all(statement);
275
+ });
276
+ router.get(
279
277
  "/api/tables/:table",
280
- validateQuery({
281
- type: "object",
282
- properties: {
283
- order: {
284
- oneOf: [
285
- { type: "string" },
286
- { type: "array", items: { type: "string" } }
287
- ]
288
- },
289
- limit: { type: "number", minimum: 1, maximum: 1e3 },
290
- page: { type: "number" },
291
- search: { type: "string" }
292
- },
293
- required: []
294
- }),
295
- route(async (req) => {
278
+ {
279
+ query: type({
280
+ order: "string|string[]?",
281
+ limit: "string.numeric?",
282
+ page: "string.numeric?",
283
+ search: "string?"
284
+ })
285
+ },
286
+ async (req) => {
296
287
  const table = stripIdentifier(req.params.table);
297
288
  const orders = queryParamToArray(req.query.order);
298
289
  const limit = parseInt(req.query.limit ?? "50");
@@ -372,28 +363,26 @@ function createRouter({
372
363
  }
373
364
  statement.append(sql` LIMIT ${limit} OFFSET ${limit * (page - 1)}`);
374
365
  return await postgres.all(statement);
375
- })
366
+ }
376
367
  );
377
- app.get(
378
- "/api/tables/:table/:pk",
379
- route(async (req) => {
380
- const table = stripIdentifier(req.params.table);
381
- const definition = await getTable(table);
382
- const primaryKey = definition.primaryKey;
383
- if (!primaryKey) {
384
- throw BadRequest(`Table ${table} has no primary key`);
385
- }
386
- const statement = sql`
368
+ router.get("/api/tables/:table/:pk", {}, async (req) => {
369
+ const table = stripIdentifier(req.params.table);
370
+ const definition = await getTable(table);
371
+ const primaryKey = definition.primaryKey;
372
+ if (!primaryKey) {
373
+ throw BadRequest(`Table ${table} has no primary key`);
374
+ }
375
+ const statement = sql`
387
376
  SELECT *
388
377
  FROM ${sql.identifier(table)}
389
378
  WHERE ${sql.identifier(primaryKey)} = ${req.params.pk}
390
379
  `;
391
- return postgres.first(statement);
392
- })
393
- );
394
- app.post(
380
+ return postgres.first(statement);
381
+ });
382
+ router.post(
395
383
  "/api/tables/:table",
396
- route(async (req) => {
384
+ { request: unknownTableRow },
385
+ async (req) => {
397
386
  const table = stripIdentifier(req.params.table);
398
387
  const definition = await getTable(table);
399
388
  const primaryKey = definition.primaryKey;
@@ -406,11 +395,12 @@ function createRouter({
406
395
  ${sql.spreadInsert(data)}
407
396
  `;
408
397
  return postgres.first(statement);
409
- })
398
+ }
410
399
  );
411
- app.put(
400
+ router.put(
412
401
  "/api/tables/:table/:pk",
413
- route(async (req) => {
402
+ { request: unknownTableRow },
403
+ async (req) => {
414
404
  const table = stripIdentifier(req.params.table);
415
405
  const pk = req.params.pk;
416
406
  const definition = await getTable(table);
@@ -440,46 +430,37 @@ function createRouter({
440
430
  RETURNING *
441
431
  `);
442
432
  });
443
- })
433
+ }
444
434
  );
445
- app.delete(
446
- "/api/tables/:table/:pk",
447
- route(async (req) => {
448
- const table = stripIdentifier(req.params.table);
449
- const pk = parseInt(req.params.pk);
450
- const definition = await getTable(table);
451
- const primaryKey = definition.primaryKey;
452
- if (!primaryKey) {
453
- throw BadRequest(`Table ${table} has no primary key`);
454
- }
455
- const statement = sql`
435
+ router.delete("/api/tables/:table/:pk", {}, async (req) => {
436
+ const table = stripIdentifier(req.params.table);
437
+ const pk = parseInt(req.params.pk);
438
+ const definition = await getTable(table);
439
+ const primaryKey = definition.primaryKey;
440
+ if (!primaryKey) {
441
+ throw BadRequest(`Table ${table} has no primary key`);
442
+ }
443
+ const statement = sql`
456
444
  DELETE FROM ${sql.identifier(table)}
457
445
  WHERE ${sql.identifier(primaryKey)} = ${pk}
458
446
  `;
459
- await postgres.query(statement);
460
- return 204;
461
- })
462
- );
463
- app.use(
464
- express.static(
447
+ await postgres.query(statement);
448
+ return 204;
449
+ });
450
+ router.use(
451
+ staticMiddleware(
465
452
  path.resolve("node_modules/@devmoods/postgres-admin/dist/client")
466
453
  )
467
454
  );
468
- app.get(
469
- "*",
470
- route(async (req, res) => {
471
- const content = await fs.readFile(
472
- "node_modules/@devmoods/postgres-admin/dist/client/_index.html",
473
- "utf-8"
474
- );
475
- return content.replace(/{{ csrf_token }}/g, generateCsrfToken(req, res)).replace(/{{ basename }}/g, basename.slice(1)).replace(/{{ title }}/g, uiOptions.title ?? "Posterboi");
476
- })
477
- );
478
- return app;
455
+ router.get("*", {}, async (req, res) => {
456
+ const content = await fs.readFile(indexHtml, "utf-8");
457
+ return content.replace(/{{ csrf_token }}/g, generateCsrfToken(req, res)).replace(/{{ basename }}/g, basename.slice(1)).replace(/{{ title }}/g, uiOptions.title ?? "Posterboi");
458
+ });
459
+ return router.getRouter();
479
460
  }
480
461
 
481
462
  // src/index.ts
482
- var admin = createRouter;
463
+ var admin = createAdminRouter;
483
464
  export {
484
465
  admin
485
466
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/postgres-admin",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -12,39 +12,32 @@
12
12
  "build:server": "tsup-node src/index.ts --format esm --dts",
13
13
  "build:client": "tsc --noEmit && vite build",
14
14
  "build": "pnpm run build:client && pnpm run build:server",
15
- "test": "vitest"
15
+ "test": "DOTENV_CONFIG_PATH=.env.test vitest"
16
16
  },
17
17
  "dependencies": {
18
+ "@devmoods/config": "0.2.0",
19
+ "@devmoods/express-extras": "0.68.0",
20
+ "@devmoods/fetch": "^4.2.0",
21
+ "@devmoods/ui": "^2.12.2",
22
+ "@tanstack/react-query": "^5.90.5",
23
+ "@types/react": "^19.2.2",
24
+ "@types/react-dom": "^19.2.2",
25
+ "@vitejs/plugin-react": "^5.0.4",
26
+ "@vitest/coverage-v8": "3.2.4",
27
+ "arktype": "^2.1.23",
28
+ "babel-plugin-react-compiler": "1.0.0",
18
29
  "date-fns": "^4.1.0",
19
- "pg-structure": "^7.15.3"
20
- },
21
- "peerDependencies": {
22
- "@devmoods/express-extras": ">=0.46.0"
23
- },
24
- "devDependencies": {
25
- "@bitiforms/adapter-devmoods-ui": "^0.15.0",
26
- "@bitiforms/react": "^0.15.0",
27
- "@devmoods/express-extras": "^0.57.3",
28
- "@devmoods/fetch": "^4.1.0",
29
- "@devmoods/ui": "^2.9.1",
30
- "@tanstack/react-query": "^5.77.2",
31
- "@types/react": "^19.1.6",
32
- "@types/react-dom": "^19.1.5",
33
- "@vitejs/plugin-react": "^4.5.0",
34
- "@vitest/coverage-v8": "3.1.4",
35
- "babel-plugin-react-compiler": "19.1.0-rc.2",
36
- "dotenv": "^16.5.0",
37
- "express": "^4.21.2",
38
- "react": "^19.1.0",
39
- "react-dom": "^19.1.0",
30
+ "dotenv": "^17.2.3",
31
+ "pg-structure": "^7.15.3",
32
+ "react": "^19.2.0",
33
+ "react-dom": "^19.2.0",
40
34
  "react-fast-compare": "^3.2.2",
41
35
  "react-icons": "^5.5.0",
42
- "react-router": "^7.6.1",
43
- "sass": "^1.89.0",
36
+ "react-router": "^7.9.4",
37
+ "sass": "^1.93.2",
44
38
  "tsup": "^8.5.0",
45
- "vite": "^6.3.5",
46
- "vitest": "^3.1.4",
47
- "zustand": "^5.0.5"
39
+ "vite": "^7.1.11",
40
+ "zustand": "^5.0.8"
48
41
  },
49
- "gitHead": "ad0fe0a15afb9540be3420360381394999f8666b"
42
+ "gitHead": "721a709c087904a5cc1332d28a62857a82295fea"
50
43
  }