express-generator-typescript 3.0.3 → 3.1.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 (37) hide show
  1. package/README.md +7 -3
  2. package/bin/cli.js +1 -20
  3. package/lib/template/README.md +4 -3
  4. package/lib/template/package.json +8 -6
  5. package/lib/template/src/common/classes/route-errors.ts +4 -8
  6. package/lib/template/src/common/constants/HttpStatusCodes.ts +2 -7
  7. package/lib/template/src/common/constants/{Paths.ts → Paths/Paths.ts} +16 -9
  8. package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
  9. package/lib/template/src/common/constants/Paths/index.ts +1 -0
  10. package/lib/template/src/common/constants/env.constants.ts +42 -0
  11. package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
  12. package/lib/template/src/entities/User/module.ts +77 -28
  13. package/lib/template/src/entities/User/types.ts +1 -1
  14. package/lib/template/src/entities/common/{types.ts → entity-base-types.ts} +1 -1
  15. package/lib/template/src/main.ts +3 -4
  16. package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
  17. package/lib/template/src/public/scripts/users.js +20 -11
  18. package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
  19. package/lib/template/src/public/stylesheets/users.css +24 -1
  20. package/lib/template/src/repos/MockOrm.ts +2 -2
  21. package/lib/template/src/repos/UserRepo.ts +20 -27
  22. package/lib/template/src/repos/common/database.json +3 -3
  23. package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +3 -11
  24. package/lib/template/src/{routes/UserRoutes.ts → routers/user.router.ts} +38 -47
  25. package/lib/template/src/server.ts +22 -11
  26. package/lib/template/src/services/UserService.ts +9 -14
  27. package/lib/template/src/views/users.html +8 -0
  28. package/lib/template/tests/support/utils.ts +23 -0
  29. package/lib/template/tests/users.test.ts +182 -140
  30. package/package.json +6 -5
  31. package/lib/template/src/common/constants/env-inv.ts +0 -32
  32. package/lib/template/src/entities/User/_internal/validators.ts +0 -47
  33. package/lib/template/src/routes/common/express-types.ts +0 -9
  34. package/lib/template/src/routes/common/parseReq.ts +0 -23
  35. package/lib/template/tests/common/comparators.ts +0 -38
  36. package/lib/template/tests/common/supertest-types.ts +0 -11
  37. /package/lib/template/src/common/{types/utility-types.ts → utils/validators.ts} +0 -0
package/README.md CHANGED
@@ -30,7 +30,7 @@ A command-line tool that generates production-ready Express projects with TypeSc
30
30
 
31
31
  ## 📦 Installation
32
32
 
33
- Requires Node.js 22.12 or newer.
33
+ Requires Node.js 22.13+ (22.x), 24.x, or 26+.
34
34
 
35
35
  ```bash
36
36
  npx express-generator-typescript
@@ -72,7 +72,9 @@ npm run dev
72
72
 
73
73
  The generated project is a small CRUD app for a `User` record. It shows how to structure models, services, and routes in Express + TypeScript. Linting, formatting, building, and hot reloading are all set up for you.
74
74
 
75
- ### Available `package.json` Scripts
75
+ ---
76
+
77
+ #### Available `package.json` scripts
76
78
 
77
79
  - `npm run dev` – Run the server in development with live reload and browser refresh.
78
80
  - `npm test` – Run the tests with Vitest.
@@ -84,7 +86,9 @@ The generated project is a small CRUD app for a `User` record. It shows how to s
84
86
  - `npm run typecheck` – Check for TypeScript errors without building.
85
87
  - `npm run install:clean` – Delete `node_modules` and the lockfile, then reinstall.
86
88
 
87
- ### Architecture
89
+ ---
90
+
91
+ #### Architecture
88
92
 
89
93
  The app uses a **layered** architecture, which suits a small CRUD app. If you plan to grow it, consider switching to a **domain-based** layout. The [Typescript Best Practices README](https://github.com/seanpmaxwell/Typescript-Best-Practices/tree/main?tab=readme-ov-file#architecture) explains both patterns.
90
94
 
package/bin/cli.js CHANGED
@@ -3,14 +3,13 @@ const path = require('path');
3
3
  const { parseArgs } = require('util');
4
4
 
5
5
  const expressGenTs = require('../lib/express-generator-typescript');
6
- const { version, engines } = require('../package.json');
6
+ const { version } = require('../package.json');
7
7
 
8
8
  // ========================================================================= //
9
9
  // CONSTANTS //
10
10
  // ========================================================================= //
11
11
 
12
12
  const DEFAULT_DEST = 'express-gen-ts';
13
- const MIN_NODE = engines.node.replace(/^>=/, '');
14
13
 
15
14
  const HELP = `Usage: express-generator-typescript [project-name] [options]
16
15
 
@@ -62,7 +61,6 @@ async function main() {
62
61
  if (positionals.length > 1) {
63
62
  throw new Error('Expected at most one project name.\n\n' + HELP);
64
63
  }
65
- assertNodeVersion();
66
64
 
67
65
  // ---- Run "express-generator-typescript"
68
66
  const dest = path.resolve(process.cwd(), positionals[0] ?? DEFAULT_DEST);
@@ -73,20 +71,3 @@ async function main() {
73
71
  });
74
72
  console.log('Project setup complete!');
75
73
  }
76
-
77
- /**
78
- * The generated project's tooling (vitest, eslint) requires a recent Node, so
79
- * fail fast instead of producing a project that can't run.
80
- *
81
- * @returns {void}
82
- */
83
- function assertNodeVersion() {
84
- const toParts = (v) => v.split('.').map(Number);
85
- const [maj, min] = toParts(process.versions.node);
86
- const [reqMaj, reqMin] = toParts(MIN_NODE);
87
- if (maj < reqMaj || (maj === reqMaj && min < reqMin)) {
88
- throw new Error(
89
- `Node ${MIN_NODE} or newer is required (found ${process.versions.node}).`,
90
- );
91
- }
92
- }
@@ -1,6 +1,6 @@
1
1
  ## About
2
2
 
3
- This project was created with [express-generator-typescript](https://github.com/seanpmaxwell/express-generator-typescript). It requires Node.js 22.12 or newer.
3
+ This project was created with [express-generator-typescript](https://github.com/seanpmaxwell/express-generator-typescript). It requires Node.js 22.13+ (22.x), 24.x, or 26+.
4
4
 
5
5
  The original template follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices).
6
6
 
@@ -52,8 +52,9 @@ Delete `node_modules/` and `package-lock.json`, then reinstall all dependencies.
52
52
  - **Logging**
53
53
  - **Request logging**: [morgan](https://github.com/expressjs/morgan) (development only)
54
54
  - **General logging**: [jet-logger](https://github.com/seanpmaxwell/jet-logger)
55
- - **Validation**: [jet-validators](https://github.com/seanpmaxwell/jet-validators)
56
- - **Environment variables**: [dotenv](https://github.com/motdotla/dotenv) loads `config/.env.*`, and [jet-env](https://github.com/seanpmaxwell/jet-env) validates them
55
+ - **Validation**: [zod](https://github.com/colinhacks/zod)
56
+ - **Middleware Validation**: [express-zod-safe](https://github.com/AngaBlue/express-zod-safe)
57
+ - **Environment variables**: [dotenv](https://github.com/motdotla/dotenv) loads `config/.env.*`
57
58
  - **Reloading**: [tsx](https://tsx.hirok.io) (`tsx watch` restarts the server) and [livereload](https://github.com/napcs/node-livereload) + [connect-livereload](https://github.com/intesso/connect-livereload) (refreshes the browser)
58
59
  - **Testing**: [Vitest](https://vitest.dev) + [Supertest](https://github.com/ladjs/supertest)
59
60
  - **Linting**: [ESLint](https://eslint.org) with [typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint) and [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n)
@@ -14,20 +14,19 @@
14
14
  "typecheck": "tsc -b --noEmit"
15
15
  },
16
16
  "engines": {
17
- "node": ">=22.12.0"
17
+ "node": "^22.13.0 || ^24.0.0 || >=26.0.0"
18
18
  },
19
19
  "dependencies": {
20
20
  "cross-env": "^10.1.0",
21
21
  "dotenv": "^18.0.4",
22
22
  "express": "^5.2.1",
23
+ "express-zod-safe": "^3.2.1",
23
24
  "helmet": "^8.3.0",
24
- "jet-env": "^1.1.6",
25
- "jet-id": "^1.3.1",
26
25
  "jet-logger": "3.0.0",
27
- "jet-paths": "^4.0.2",
28
- "jet-validators": "^2.3.1",
29
26
  "jsonfile": "^6.2.1",
30
- "morgan": "^1.12.1"
27
+ "morgan": "^1.12.1",
28
+ "uuid": "^14.0.2",
29
+ "zod": "^4.6.5"
31
30
  },
32
31
  "devDependencies": {
33
32
  "@eslint/js": "^10.0.1",
@@ -53,5 +52,8 @@
53
52
  "typescript": "^6.0.3",
54
53
  "typescript-eslint": "^8.70.1",
55
54
  "vitest": "^5.0.2"
55
+ },
56
+ "allowScripts": {
57
+ "esbuild@0.28.2": true
56
58
  }
57
59
  }
@@ -1,5 +1,3 @@
1
- import type { ParseError } from 'jet-validators/utils';
2
-
3
1
  import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
4
2
 
5
3
  // ========================================================================= //
@@ -26,12 +24,10 @@ export class RouteError extends Error {
26
24
  }
27
25
 
28
26
  /**
29
- * Request data failed schema validation.
27
+ * Don't have to manually pass `HttpStatusCodes.NOT_FOUND` each time.
30
28
  */
31
- export class ValidationError extends RouteError {
32
- public static MESSAGE = 'Request validation failed.';
33
-
34
- public constructor(errors: ParseError[]) {
35
- super(HttpStatusCodes.BAD_REQUEST, ValidationError.MESSAGE, errors);
29
+ export class NotFoundError extends RouteError {
30
+ public constructor(message: string) {
31
+ super(HttpStatusCodes.NOT_FOUND, message);
36
32
  }
37
33
  }
@@ -1,4 +1,4 @@
1
- import type { ValueOf } from '../types/utility-types';
1
+ import type { ValueOf } from '../utils/validators';
2
2
 
3
3
  // ========================================================================= //
4
4
  // CONSTANTS //
@@ -385,14 +385,9 @@ const HttpStatusCodes = {
385
385
  NETWORK_AUTHENTICATION_REQUIRED: 511,
386
386
  } as const;
387
387
 
388
- // ========================================================================= //
389
- // TYPES //
390
- // ========================================================================= //
391
-
392
- type HttpStatusCodes = ValueOf<typeof HttpStatusCodes>;
393
-
394
388
  // ========================================================================= //
395
389
  // EXPORT //
396
390
  // ========================================================================= //
397
391
 
392
+ type HttpStatusCodes = ValueOf<typeof HttpStatusCodes>;
398
393
  export default HttpStatusCodes;
@@ -1,19 +1,26 @@
1
- import jetPaths from 'jet-paths';
1
+ import { ep, pp } from './helpers';
2
2
 
3
3
  // ========================================================================= //
4
4
  // CONSTANTS //
5
5
  // ========================================================================= //
6
6
 
7
- const Paths = jetPaths({
8
- _: '/api',
7
+ const BASE = '/api';
8
+ const USERS_BASE = '/users';
9
+ const USERS = BASE + USERS_BASE;
10
+
11
+ const Paths = {
12
+ PATH: BASE,
9
13
  Users: {
10
- _: '/users',
11
- Get: '/all',
12
- Add: '/add',
13
- Update: '/update',
14
- Delete: '/delete',
14
+ PATH: USERS_BASE,
15
+ Get: ep(USERS, '/all', 'GET'),
16
+ Add: ep(USERS, '/add', 'POST'),
17
+ Update: ep(USERS, '/update', 'PUT'),
18
+ Delete: {
19
+ ...ep(USERS, `/delete/:id`, 'DELETE'),
20
+ format: (id: string) => `${USERS}/delete/${pp(id)}`,
21
+ },
15
22
  },
16
- } as const);
23
+ } as const;
17
24
 
18
25
  // ========================================================================= //
19
26
  // EXPORT //
@@ -0,0 +1,29 @@
1
+ // ========================================================================= //
2
+ // TYPES //
3
+ // ========================================================================= //
4
+
5
+ type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+
7
+ // ========================================================================= //
8
+ // FUNCTIONS //
9
+ // ========================================================================= //
10
+
11
+ /**
12
+ * `ep` stands for "Endpoint"
13
+ */
14
+ export function ep<V extends HttpVerb>(base: string, path: string, verb: V) {
15
+ const template = `${base}${path}`;
16
+ return {
17
+ PATH: path,
18
+ FULL_PATH: template,
19
+ VERB: verb,
20
+ DISPLAY: `<${verb}>${template}`,
21
+ } as const;
22
+ }
23
+
24
+ /**
25
+ * `pp` stands for path params
26
+ */
27
+ export function pp(param: string | number | boolean): string {
28
+ return encodeURIComponent(param);
29
+ }
@@ -0,0 +1 @@
1
+ export { default as default } from './Paths';
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-process-env */
2
+ import logger from 'jet-logger';
3
+ import { z } from 'zod';
4
+
5
+ // ========================================================================= //
6
+ // CONSTANTS //
7
+ // ========================================================================= //
8
+
9
+ // NOTE: These need to match the names of your ".env" files
10
+ export const NodeEnvs = {
11
+ DEVELOPMENT: 'development',
12
+ TEST: 'test',
13
+ PRODUCTION: 'production',
14
+ } as const;
15
+ export type NodeEnvs = z.infer<typeof NodeEnvsEnum>;
16
+
17
+ export const Env = {
18
+ NODE_ENV: process.env.NODE_ENV?.toLowerCase(),
19
+ PORT: Number(process.env.PORT),
20
+ } as const;
21
+
22
+ // ========================================================================= //
23
+ // EXEC //
24
+ // ========================================================================= //
25
+
26
+ const NodeEnvsEnum = z.enum(NodeEnvs, 'Not a valid node environment');
27
+
28
+ const EnvSchema = z.object({
29
+ NODE_ENV: NodeEnvsEnum,
30
+ PORT: z.uint32('Express port must be a positive integer'),
31
+ });
32
+
33
+ // ===================== Validate Environment Variables ==================== //
34
+
35
+ {
36
+ try {
37
+ EnvSchema.parse(Env);
38
+ } catch (err) {
39
+ logger.err(err);
40
+ throw err;
41
+ }
42
+ }
@@ -1,3 +1,5 @@
1
+ import { z } from 'zod';
2
+
1
3
  // ========================================================================= //
2
4
  // TYPES //
3
5
  // ========================================================================= //
@@ -5,6 +7,14 @@
5
7
  export type ISOString =
6
8
  `${number}-${number}-${number}T${number}:${number}:${number}${string}`;
7
9
 
10
+ // ========================================================================= //
11
+ // EXEC //
12
+ // ========================================================================= //
13
+
14
+ export const ISOStringSchema = z.iso
15
+ .datetime()
16
+ .transform((value) => value as ISOString);
17
+
8
18
  // ========================================================================= //
9
19
  // FUNCTIONS //
10
20
  // ========================================================================= //
@@ -15,15 +25,3 @@ export type ISOString =
15
25
  export function getISOString(date = new Date()): ISOString {
16
26
  return date.toISOString() as ISOString;
17
27
  }
18
-
19
- /**
20
- * Validate an ISOString.
21
- */
22
- export function isISOString(value: unknown): value is ISOString {
23
- try {
24
- const date = new Date(value as string);
25
- return !isNaN(date.getTime()) && date.toISOString() === value;
26
- } catch {
27
- return false;
28
- }
29
- }
@@ -1,30 +1,45 @@
1
- import jetid from 'jet-id';
2
-
3
- import { getISOString, type ISOString } from '@src/common/utils/date-utils';
1
+ import { v7 as uuidv7 } from 'uuid';
2
+ import { z } from 'zod';
4
3
 
5
4
  import {
6
- isCompleteUser,
7
- isUserId,
8
- isUserInput,
9
- parseUser,
10
- } from './_internal/validators';
11
- import type { UserEntity } from './types';
5
+ getISOString,
6
+ type ISOString,
7
+ ISOStringSchema,
8
+ } from '@src/common/utils/date.utils';
9
+
10
+ import type { UserEntity, UserInput } from './types';
12
11
 
13
12
  // ========================================================================= //
14
- // FUNCTIONS //
13
+ // CONSTANTS //
15
14
  // ========================================================================= //
16
15
 
17
- /**
18
- * Get a new `UserEntity` object with default values.
19
- */
20
- function getDefaults(): UserEntity {
21
- return {
22
- id: jetid(),
23
- name: '',
24
- email: '',
25
- created: getISOString(),
26
- };
27
- }
16
+ /** @ValueFactoryFunction */
17
+ const UserDefaults = () => ({
18
+ id: genId(),
19
+ name: '--',
20
+ email: '--@example.com',
21
+ created: getISOString(),
22
+ });
23
+
24
+ // ========================================================================= //
25
+ // EXEC //
26
+ // ========================================================================= //
27
+
28
+ const UserIdSchema = z.uuidv7('Not a valid uuid7 id');
29
+
30
+ const UserInputSchema = z.object({
31
+ name: z.string().min(1, 'Name cannot be empty'),
32
+ email: z.email('Not a valid email'),
33
+ }) satisfies z.ZodType<UserInput>;
34
+
35
+ const UserSchema = UserInputSchema.extend({
36
+ id: UserIdSchema,
37
+ created: ISOStringSchema,
38
+ }) satisfies z.ZodType<UserEntity>;
39
+
40
+ // ========================================================================= //
41
+ // FUNCTIONS //
42
+ // ========================================================================= //
28
43
 
29
44
  /**
30
45
  * Factory-function.
@@ -32,9 +47,7 @@ function getDefaults(): UserEntity {
32
47
  * Create a `UserEntity` from a partial or `undefined`
33
48
  */
34
49
  function create(user?: Partial<UserEntity>): UserEntity {
35
- return parseUser({ ...getDefaults(), ...user }, (errors) => {
36
- throw new Error('Setup new user failed ' + JSON.stringify(errors, null, 2));
37
- });
50
+ return { ...UserDefaults(), ...user };
38
51
  }
39
52
 
40
53
  /**
@@ -43,13 +56,41 @@ function create(user?: Partial<UserEntity>): UserEntity {
43
56
  * Create a `UserEntity` from individual properties
44
57
  */
45
58
  function of(name: string, email?: string, created?: ISOString): UserEntity {
46
- const retVal = getDefaults();
59
+ const retVal = UserDefaults();
47
60
  if (name) retVal.name = name;
48
61
  if (email) retVal.email = email;
49
62
  if (created) retVal.created = created;
50
63
  return retVal;
51
64
  }
52
65
 
66
+ /**
67
+ * Generate a uuid7 id.
68
+ */
69
+ function genId(): string {
70
+ return uuidv7();
71
+ }
72
+
73
+ /**
74
+ * Check if a valid user object
75
+ */
76
+ function isUser(val: unknown): val is UserEntity {
77
+ return UserSchema.safeParse(val).success;
78
+ }
79
+
80
+ /**
81
+ * Check if a valid end users input fields are valid.
82
+ */
83
+ function isUserInput(val: unknown): val is UserInput {
84
+ return UserInputSchema.safeParse(val).success;
85
+ }
86
+
87
+ /**
88
+ * Test if an id is a valid user id.
89
+ */
90
+ function isUserId(val: unknown): val is string {
91
+ return UserIdSchema.safeParse(val).success;
92
+ }
93
+
53
94
  // ========================================================================= //
54
95
  // EXPORT //
55
96
  // ========================================================================= //
@@ -57,7 +98,15 @@ function of(name: string, email?: string, created?: ISOString): UserEntity {
57
98
  export default {
58
99
  of,
59
100
  create,
60
- isId: isUserId,
61
- isComplete: isCompleteUser,
62
- isInput: isUserInput,
101
+ genId,
102
+ is: {
103
+ id: isUserId,
104
+ user: isUser,
105
+ input: isUserInput,
106
+ },
107
+ Schemas: {
108
+ Main: UserSchema,
109
+ Input: UserInputSchema,
110
+ Id: UserIdSchema,
111
+ },
63
112
  } as const;
@@ -1,4 +1,4 @@
1
- import type { Entity } from '@src/entities/common/types';
1
+ import type { Entity } from '@src/entities/common/entity-base-types';
2
2
 
3
3
  // ========================================================================= //
4
4
  // TYPES //
@@ -1,4 +1,4 @@
1
- import type { ISOString } from '@src/common/utils/date-utils';
1
+ import type { ISOString } from '@src/common/utils/date.utils';
2
2
 
3
3
  // ========================================================================= //
4
4
  // TYPES //
@@ -1,21 +1,20 @@
1
1
  import logger from 'jet-logger';
2
2
 
3
- import { EnvVars } from './common/constants/env-inv';
3
+ import { Env } from './common/constants/env.constants';
4
4
  import server from './server';
5
5
 
6
6
  // ========================================================================= //
7
7
  // CONSTANTS //
8
8
  // ========================================================================= //
9
9
 
10
- const SERVER_START_MESSAGE =
11
- 'Express server started on port: ' + EnvVars.Port.toString();
10
+ const SERVER_START_MESSAGE = `Express server started on port: ${Env.PORT}`;
12
11
 
13
12
  // ========================================================================= //
14
13
  // EXEC //
15
14
  // ========================================================================= //
16
15
 
17
16
  // Start the server
18
- server.listen(EnvVars.Port, (err) => {
17
+ server.listen(Env.PORT, (err) => {
19
18
  if (!!err) {
20
19
  logger.err(err.message);
21
20
  } else {
@@ -4226,4 +4226,3 @@
4226
4226
  }
4227
4227
  );
4228
4228
  });
4229
- //# sourceMappingURL=bootstrap.bundle.min.js.map
@@ -64,9 +64,6 @@ function onDocumentClick(event) {
64
64
  function addUser() {
65
65
  var nameInput = document.getElementById('name-input');
66
66
  var emailInput = document.getElementById('email-input');
67
- if (!nameInput.value || !emailInput.value) {
68
- return;
69
- }
70
67
  var data = {
71
68
  user: {
72
69
  name: nameInput.value,
@@ -154,7 +151,7 @@ function submitEdit(ele) {
154
151
  */
155
152
  function deleteUser(ele) {
156
153
  var id = ele.getAttribute('data-user-id');
157
- HttpClient.delete('/api/users/delete?id=' + encodeURIComponent(id))
154
+ HttpClient.delete('/api/users/delete/' + encodeURIComponent(id))
158
155
  .then(checkResponse)
159
156
  .then(() => displayUsers())
160
157
  .catch(showError);
@@ -163,7 +160,7 @@ function deleteUser(ele) {
163
160
  // ============================= Shared Helpers ============================ //
164
161
 
165
162
  /**
166
- * Reject non-2xx responses with the server's error message.
163
+ * Log failed responses and their full JSON bodies, then reject the request.
167
164
  *
168
165
  * Used by:
169
166
  * {@link displayUsers}
@@ -175,14 +172,25 @@ function deleteUser(ele) {
175
172
  * @returns {Response | Promise<never>} The response when it succeeded.
176
173
  */
177
174
  function checkResponse(resp) {
178
- if (resp.ok) {
179
- return resp;
180
- }
175
+ if (resp.ok) return resp;
181
176
  return resp
182
177
  .json()
183
178
  .catch(() => ({}))
184
179
  .then((body) => {
185
- throw new Error(body.error || 'Request failed: ' + resp.status);
180
+ throw JSON.stringify(
181
+ {
182
+ url: resp.url,
183
+ status: resp.status,
184
+ statusText: resp.statusText,
185
+ ok: resp.ok,
186
+ redirected: resp.redirected,
187
+ type: resp.type,
188
+ headers: Object.fromEntries(resp.headers.entries()),
189
+ body,
190
+ },
191
+ null,
192
+ 2,
193
+ );
186
194
  });
187
195
  }
188
196
 
@@ -195,9 +203,10 @@ function checkResponse(resp) {
195
203
  * {@link submitEdit}
196
204
  * {@link deleteUser}
197
205
  *
198
- * @param {Error} err
206
+ * @param {string | Error} err
199
207
  * @returns {void}
200
208
  */
201
209
  function showError(err) {
202
- alert(err.message);
210
+ document.getElementById('error-display').textContent =
211
+ typeof err === 'string' ? err : err.stack || err.message;
203
212
  }
@@ -11795,4 +11795,3 @@ fieldset:disabled .btn {
11795
11795
  display: none !important;
11796
11796
  }
11797
11797
  }
11798
- /*# sourceMappingURL=bootstrap.min.css.map */
@@ -1,4 +1,7 @@
1
1
  body {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ gap: 2em;
2
5
  padding: 100px;
3
6
  font:
4
7
  14px 'Lucida Grande',
@@ -9,7 +12,6 @@ body {
9
12
 
10
13
  body .users-column {
11
14
  display: inline-block;
12
- margin-right: 2em;
13
15
  vertical-align: top;
14
16
  }
15
17
 
@@ -48,3 +50,24 @@ body .users-column .user-display-ele button {
48
50
  body .users-column .user-display-ele .edit-view {
49
51
  display: none;
50
52
  }
53
+
54
+ /** Error Display Column **/
55
+
56
+ body .errors-column {
57
+ flex: 1 1 320px;
58
+ min-width: 0;
59
+ color: #a61b29;
60
+ }
61
+
62
+ body .errors-column #error-display {
63
+ margin: 0;
64
+ padding: 16px;
65
+ border: 1px solid #e5a4aa;
66
+ border-radius: 6px;
67
+ background-color: #fff2f3;
68
+ color: inherit;
69
+ white-space: pre-wrap;
70
+ overflow-wrap: anywhere;
71
+ max-height: 65vh;
72
+ overflow-y: auto;
73
+ }
@@ -2,7 +2,7 @@ import fs from 'fs/promises';
2
2
  import jsonfile from 'jsonfile';
3
3
  import path from 'path';
4
4
 
5
- import { EnvVars, NodeEnvs } from '@src/common/constants/env-inv';
5
+ import { Env, NodeEnvs } from '@src/common/constants/env.constants';
6
6
  import type { UserEntity } from '@src/entities/User';
7
7
 
8
8
  // ========================================================================= //
@@ -12,7 +12,7 @@ import type { UserEntity } from '@src/entities/User';
12
12
  const DATABASE_FILE_PATH = path.join(
13
13
  import.meta.dirname,
14
14
  'common',
15
- EnvVars.NodeEnv === NodeEnvs.TEST ? 'database.test.json' : 'database.json',
15
+ Env.NODE_ENV === NodeEnvs.TEST ? 'database.test.json' : 'database.json',
16
16
  );
17
17
 
18
18
  // ========================================================================= //