express-generator-typescript 2.7.8 → 2.7.10

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 (43) hide show
  1. package/bin/cli.js +1 -1
  2. package/lib/project-files/README.md +65 -0
  3. package/lib/project-files/config/.env.development +15 -0
  4. package/lib/project-files/config/.env.production +15 -0
  5. package/lib/project-files/config/.env.test +14 -0
  6. package/lib/project-files/config.ts +22 -0
  7. package/lib/project-files/eslint.config.ts +100 -0
  8. package/lib/project-files/gitignore +6 -0
  9. package/lib/project-files/package.json +56 -0
  10. package/lib/project-files/scripts/build.ts +28 -0
  11. package/lib/project-files/scripts/common/utils.ts +93 -0
  12. package/lib/project-files/src/common/constants/ENV.ts +19 -0
  13. package/lib/project-files/src/common/constants/HTTP_STATUS_CODES.ts +81 -0
  14. package/lib/project-files/src/common/constants/PATHS.ts +15 -0
  15. package/lib/project-files/src/common/constants/index.ts +11 -0
  16. package/lib/project-files/src/common/util/misc.ts +11 -0
  17. package/lib/project-files/src/common/util/route-errors.ts +38 -0
  18. package/lib/project-files/src/common/util/validators.ts +14 -0
  19. package/lib/project-files/src/index.ts +27 -0
  20. package/lib/project-files/src/models/User.ts +70 -0
  21. package/lib/project-files/src/models/common/types.ts +5 -0
  22. package/lib/project-files/src/public/stylesheets/users.css +48 -0
  23. package/lib/project-files/src/repos/MockOrm.ts +63 -0
  24. package/lib/project-files/src/repos/UserRepo.ts +133 -0
  25. package/lib/project-files/src/repos/database.json +1 -0
  26. package/lib/project-files/src/repos/database.test.json +1 -0
  27. package/lib/project-files/src/routes/UserRoutes.ts +69 -0
  28. package/lib/project-files/src/routes/common/types/index.ts +10 -0
  29. package/lib/project-files/src/routes/common/util/index.ts +16 -0
  30. package/lib/project-files/src/routes/index.ts +31 -0
  31. package/lib/project-files/src/server.ts +86 -0
  32. package/lib/project-files/src/services/UserService.ts +70 -0
  33. package/lib/project-files/src/views/users.html +132 -0
  34. package/lib/project-files/tests/aux/setup.ts +26 -0
  35. package/lib/project-files/tests/common/types.ts +16 -0
  36. package/lib/project-files/tests/common/utils.ts +55 -0
  37. package/lib/project-files/tests/users.test.ts +123 -0
  38. package/lib/project-files/tsconfig.json +35 -0
  39. package/lib/project-files/tsconfig.prod.json +30 -0
  40. package/lib/project-files/vitest.config.mts +18 -0
  41. package/package.json +1 -1
  42. package/README-git +0 -78
  43. package/express-typescript.png +0 -0
@@ -0,0 +1,70 @@
1
+ import { isString, isUnsignedInteger } from 'jet-validators';
2
+ import { parseObject, OnErrorCallback } from 'jet-validators/utils';
3
+
4
+ import { transformIsDate } from '@src/common/util/validators';
5
+ import { IModel } from './common/types';
6
+
7
+ /******************************************************************************
8
+ Constants
9
+ ******************************************************************************/
10
+
11
+ const DEFAULT_USER_VALS: IUser = {
12
+ id: 0,
13
+ name: '',
14
+ created: new Date(),
15
+ email: '',
16
+ } as const;
17
+
18
+ /******************************************************************************
19
+ Types
20
+ ******************************************************************************/
21
+
22
+ export interface IUser extends IModel {
23
+ name: string;
24
+ email: string;
25
+ }
26
+
27
+ /******************************************************************************
28
+ Setup
29
+ ******************************************************************************/
30
+
31
+ // Initialize the "parseUser" function
32
+ const parseUser = parseObject<IUser>({
33
+ id: isUnsignedInteger,
34
+ name: isString,
35
+ email: isString,
36
+ created: transformIsDate,
37
+ });
38
+
39
+ /******************************************************************************
40
+ Functions
41
+ ******************************************************************************/
42
+
43
+ /**
44
+ * New user object.
45
+ */
46
+ function __new__(user?: Partial<IUser>): IUser {
47
+ const defaults = { ...DEFAULT_USER_VALS };
48
+ defaults.created = new Date();
49
+ return parseUser({ ...defaults, ...user }, errors => {
50
+ throw new Error('Setup new user failed ' + JSON.stringify(errors, null, 2));
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Check is a user object. For the route validation.
56
+ */
57
+
58
+ // because this isn't marked with the symbol, the errors are not bubbling up
59
+ function test(arg: unknown, errCb?: OnErrorCallback): arg is IUser {
60
+ return !!parseUser(arg, errCb);
61
+ }
62
+
63
+ /******************************************************************************
64
+ Export default
65
+ ******************************************************************************/
66
+
67
+ export default {
68
+ new: __new__,
69
+ test,
70
+ } as const;
@@ -0,0 +1,5 @@
1
+
2
+ export interface IModel {
3
+ id: number;
4
+ created: Date;
5
+ }
@@ -0,0 +1,48 @@
1
+ body {
2
+ padding: 100px;
3
+ font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4
+ }
5
+
6
+ body .users-column {
7
+ display: inline-block;
8
+ margin-right: 2em;
9
+ vertical-align: top;
10
+ }
11
+
12
+ body .users-column .column-header {
13
+ padding-bottom: 5px;
14
+ font-weight: 700;
15
+ font-size: 1.2em;
16
+ }
17
+
18
+
19
+ /** Add User Column **/
20
+
21
+ body .add-user-col input {
22
+ margin-bottom: 10px;
23
+ }
24
+
25
+ body .add-user-col #add-user-btn {
26
+ margin-top: 2px;
27
+ margin-bottom: 10px;
28
+ }
29
+
30
+
31
+ /** Users Display Column **/
32
+
33
+ .email-edit {
34
+ padding-bottom: 8px;
35
+ }
36
+
37
+ body .users-column .user-display-ele {
38
+ padding-bottom: 10px;
39
+ }
40
+
41
+ body .users-column .user-display-ele button {
42
+ margin-top: 2px;
43
+ margin-bottom: 10px;
44
+ }
45
+
46
+ body .users-column .user-display-ele .edit-view {
47
+ display: none;
48
+ }
@@ -0,0 +1,63 @@
1
+ import jsonfile from 'jsonfile';
2
+
3
+ import ENV from '@src/common/constants/ENV';
4
+ import { NODE_ENVS } from '@src/common/constants';
5
+ import { IUser } from '@src/models/User';
6
+
7
+
8
+ /******************************************************************************
9
+ Constants
10
+ ******************************************************************************/
11
+
12
+ const DB_FILE_NAME = (
13
+ ENV.NodeEnv === NODE_ENVS.Test
14
+ ? 'database.test.json'
15
+ : 'database.json'
16
+ );
17
+
18
+
19
+ /******************************************************************************
20
+ Types
21
+ ******************************************************************************/
22
+
23
+ interface IDatabase {
24
+ users: IUser[];
25
+ }
26
+
27
+
28
+ /******************************************************************************
29
+ Functions
30
+ ******************************************************************************/
31
+
32
+ /**
33
+ * Fetch the json from the file.
34
+ */
35
+ function openDb(): Promise<IDatabase> {
36
+ return jsonfile.readFile(__dirname + '/' + DB_FILE_NAME) as
37
+ Promise<IDatabase>;
38
+ }
39
+
40
+ /**
41
+ * Update the file.
42
+ */
43
+ function saveDb(db: IDatabase): Promise<void> {
44
+ return jsonfile.writeFile((__dirname + '/' + DB_FILE_NAME), db);
45
+ }
46
+
47
+ /**
48
+ * Empty the database
49
+ */
50
+ function cleanDb(): Promise<void> {
51
+ return jsonfile.writeFile((__dirname + '/' + DB_FILE_NAME), {});
52
+ }
53
+
54
+
55
+ /******************************************************************************
56
+ Export default
57
+ ******************************************************************************/
58
+
59
+ export default {
60
+ openDb,
61
+ saveDb,
62
+ cleanDb,
63
+ } as const;
@@ -0,0 +1,133 @@
1
+ import { IUser } from '@src/models/User';
2
+ import { getRandomInt } from '@src/common/util/misc';
3
+
4
+ import orm from './MockOrm';
5
+
6
+ /******************************************************************************
7
+ Functions
8
+ ******************************************************************************/
9
+
10
+ /**
11
+ * Get one user.
12
+ */
13
+ async function getOne(email: string): Promise<IUser | null> {
14
+ const db = await orm.openDb();
15
+ for (const user of db.users) {
16
+ if (user.email === email) {
17
+ return user;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ /**
24
+ * See if a user with the given id exists.
25
+ */
26
+ async function persists(id: number): Promise<boolean> {
27
+ const db = await orm.openDb();
28
+ for (const user of db.users) {
29
+ if (user.id === id) {
30
+ return true;
31
+ }
32
+ }
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * Get all users.
38
+ */
39
+ async function getAll(): Promise<IUser[]> {
40
+ const db = await orm.openDb();
41
+ return db.users;
42
+ }
43
+
44
+ /**
45
+ * Add one user.
46
+ */
47
+ async function add(user: IUser): Promise<void> {
48
+ const db = await orm.openDb();
49
+ user.id = getRandomInt();
50
+ db.users.push(user);
51
+ return orm.saveDb(db);
52
+ }
53
+
54
+ /**
55
+ * Update a user.
56
+ */
57
+ async function update(user: IUser): Promise<void> {
58
+ const db = await orm.openDb();
59
+ for (let i = 0; i < db.users.length; i++) {
60
+ if (db.users[i].id === user.id) {
61
+ const dbUser = db.users[i];
62
+ db.users[i] = {
63
+ ...dbUser,
64
+ name: user.name,
65
+ email: user.email,
66
+ };
67
+ return orm.saveDb(db);
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Delete one user.
74
+ */
75
+ async function __delete__(id: number): Promise<void> {
76
+ const db = await orm.openDb();
77
+ for (let i = 0; i < db.users.length; i++) {
78
+ if (db.users[i].id === id) {
79
+ db.users.splice(i, 1);
80
+ return orm.saveDb(db);
81
+ }
82
+ }
83
+ }
84
+
85
+ // **** Unit-Tests Only **** //
86
+
87
+ /**
88
+ * Delete every user record.
89
+ *
90
+ * @internal
91
+ * Test-only helper. Do not use in production code.
92
+ */
93
+ async function deleteAllUsers(): Promise<void> {
94
+ const db = await orm.openDb();
95
+ db.users = [];
96
+ return orm.saveDb(db);
97
+ }
98
+
99
+ /**
100
+ * Insert multiple users. Can't do multiple at once cause using a plain file
101
+ * for now.
102
+ *
103
+ * @internal
104
+ * Test-only helper. Do not use in production code.
105
+ */
106
+ async function insertMultiple(
107
+ users: IUser[] | readonly IUser[],
108
+ ): Promise<IUser[]> {
109
+ const db = await orm.openDb(),
110
+ usersF = [ ...users ];
111
+ for (const user of usersF) {
112
+ user.id = getRandomInt();
113
+ user.created = new Date();
114
+ }
115
+ db.users = [ ...db.users, ...users ];
116
+ await orm.saveDb(db);
117
+ return usersF;
118
+ }
119
+
120
+ /******************************************************************************
121
+ Export default
122
+ ******************************************************************************/
123
+
124
+ export default {
125
+ getOne,
126
+ persists,
127
+ getAll,
128
+ add,
129
+ update,
130
+ delete: __delete__,
131
+ deleteAllUsers,
132
+ insertMultiple,
133
+ } as const;
@@ -0,0 +1 @@
1
+ {"users":[{"id":366115170645,"name":"Sean Maxwell","email":"smaxwell@example.com","created":"2024-03-22T05:14:36.252Z"},{"id":310946254456,"name":"John Smith","email":"john.smith@example.com","created":"2024-03-22T05:20:55.079Z"},{"id":143027113460,"name":"Gordan Freeman","email":"nova@prospect.com","created":"2024-03-22T05:42:18.895Z"}]}
@@ -0,0 +1 @@
1
+ {"users":[{"id":169238117092,"name":"Bill","email":"sean.maxwell@gmail.com","created":"2026-01-01T23:26:38.953Z"},{"id":463030566103,"name":"John Smith","email":"john.smith@gmail.com","created":"2026-01-01T23:26:38.953Z"},{"id":420935775197,"name":"Gordan Freeman","email":"gordan.freeman@gmail.com","created":"2026-01-01T23:26:38.953Z"}]}
@@ -0,0 +1,69 @@
1
+ import { isNumber } from 'jet-validators';
2
+ import { transform } from 'jet-validators/utils';
3
+
4
+ import HTTP_STATUS_CODES from '@src/common/constants/HTTP_STATUS_CODES';
5
+ import UserService from '@src/services/UserService';
6
+ import User from '@src/models/User';
7
+
8
+ import { IReq, IRes } from './common/types';
9
+ import { parseReq } from './common/util';
10
+
11
+ /******************************************************************************
12
+ Constants
13
+ ******************************************************************************/
14
+
15
+ const Validators = {
16
+ add: parseReq({ user: User.test }),
17
+ update: parseReq({ user: User.test }),
18
+ delete: parseReq({ id: transform(Number, isNumber) }),
19
+ } as const;
20
+
21
+ /******************************************************************************
22
+ Functions
23
+ ******************************************************************************/
24
+
25
+ /**
26
+ * Get all users.
27
+ */
28
+ async function getAll(_: IReq, res: IRes) {
29
+ const users = await UserService.getAll();
30
+ res.status(HTTP_STATUS_CODES.Ok).json({ users });
31
+ }
32
+
33
+ /**
34
+ * Add one user.
35
+ */
36
+ async function add(req: IReq, res: IRes) {
37
+ const { user } = Validators.add(req.body);
38
+ await UserService.addOne(user);
39
+ res.status(HTTP_STATUS_CODES.Created).end();
40
+ }
41
+
42
+ /**
43
+ * Update one user.
44
+ */
45
+ async function update(req: IReq, res: IRes) {
46
+ const { user } = Validators.update(req.body);
47
+ await UserService.updateOne(user);
48
+ res.status(HTTP_STATUS_CODES.Ok).end();
49
+ }
50
+
51
+ /**
52
+ * Delete one user.
53
+ */
54
+ async function delete_(req: IReq, res: IRes) {
55
+ const { id } = Validators.delete(req.params);
56
+ await UserService.delete(id);
57
+ res.status(HTTP_STATUS_CODES.Ok).end();
58
+ }
59
+
60
+ /******************************************************************************
61
+ Export default
62
+ ******************************************************************************/
63
+
64
+ export default {
65
+ getAll,
66
+ add,
67
+ update,
68
+ delete: delete_,
69
+ } as const;
@@ -0,0 +1,10 @@
1
+ import { Response, Request } from 'express';
2
+
3
+ /******************************************************************************
4
+ Types
5
+ ******************************************************************************/
6
+
7
+ type TRecord = Record<string, unknown>;
8
+ export type IReq = Request<TRecord, void, TRecord, TRecord>;
9
+ export type IRes = Response<unknown, TRecord>;
10
+
@@ -0,0 +1,16 @@
1
+ import { parseObject, Schema } from 'jet-validators/utils';
2
+ import { ValidationError } from '@src/common/util/route-errors';
3
+
4
+ /******************************************************************************
5
+ Functions
6
+ ******************************************************************************/
7
+
8
+ /**
9
+ * Throw a "ParseObjError" when "parseObject" fails. Also extract a nested
10
+ * "ParseObjError" and add it to the nestedErrors array.
11
+ */
12
+ export function parseReq<U extends Schema>(schema: U) {
13
+ return parseObject(schema, errors => {
14
+ throw new ValidationError(errors);
15
+ });
16
+ }
@@ -0,0 +1,31 @@
1
+ import { Router } from 'express';
2
+
3
+ import PATHS from '@src/common/constants/PATHS';
4
+ import UserRoutes from './UserRoutes';
5
+
6
+ /******************************************************************************
7
+ Setup
8
+ ******************************************************************************/
9
+
10
+ const apiRouter = Router();
11
+
12
+
13
+ // ** Add UserRouter ** //
14
+
15
+ // Init router
16
+ const userRouter = Router();
17
+
18
+ // Get all users
19
+ userRouter.get(PATHS.Users.Get, UserRoutes.getAll);
20
+ userRouter.post(PATHS.Users.Add, UserRoutes.add);
21
+ userRouter.put(PATHS.Users.Update, UserRoutes.update);
22
+ userRouter.delete(PATHS.Users.Delete, UserRoutes.delete);
23
+
24
+ // Add UserRouter
25
+ apiRouter.use(PATHS.Users._, userRouter);
26
+
27
+ /******************************************************************************
28
+ Export default
29
+ ******************************************************************************/
30
+
31
+ export default apiRouter;
@@ -0,0 +1,86 @@
1
+ import morgan from 'morgan';
2
+ import path from 'path';
3
+ import helmet from 'helmet';
4
+ import express, { Request, Response, NextFunction } from 'express';
5
+ import logger from 'jet-logger';
6
+
7
+ import BaseRouter from '@src/routes';
8
+
9
+ import Paths from '@src/common/constants/PATHS';
10
+ import ENV from '@src/common/constants/ENV';
11
+ import HTTP_STATUS_CODES, {
12
+ HttpStatusCodes,
13
+ } from '@src/common/constants/HTTP_STATUS_CODES';
14
+ import { RouteError } from '@src/common/util/route-errors';
15
+ import { NODE_ENVS } from '@src/common/constants';
16
+
17
+
18
+ /******************************************************************************
19
+ Setup
20
+ ******************************************************************************/
21
+
22
+ const app = express();
23
+
24
+
25
+ // **** Middleware **** //
26
+
27
+ // Basic middleware
28
+ app.use(express.json());
29
+ app.use(express.urlencoded({extended: true}));
30
+
31
+ // Show routes called in console during development
32
+ if (ENV.NodeEnv === NODE_ENVS.Dev) {
33
+ app.use(morgan('dev'));
34
+ }
35
+
36
+ // Security
37
+ if (ENV.NodeEnv === NODE_ENVS.Production) {
38
+ // eslint-disable-next-line n/no-process-env
39
+ if (!process.env.DISABLE_HELMET) {
40
+ app.use(helmet());
41
+ }
42
+ }
43
+
44
+ // Add APIs, must be after middleware
45
+ app.use(Paths._, BaseRouter);
46
+
47
+ // Add error handler
48
+ app.use((err: Error, _: Request, res: Response, next: NextFunction) => {
49
+ if (ENV.NodeEnv !== NODE_ENVS.Test.valueOf()) {
50
+ logger.err(err, true);
51
+ }
52
+ let status: HttpStatusCodes = HTTP_STATUS_CODES.BadRequest;
53
+ if (err instanceof RouteError) {
54
+ status = err.status;
55
+ res.status(status).json({ error: err.message });
56
+ }
57
+ return next(err);
58
+ });
59
+
60
+
61
+ // **** FrontEnd Content **** //
62
+
63
+ // Set views directory (html)
64
+ const viewsDir = path.join(__dirname, 'views');
65
+ app.set('views', viewsDir);
66
+
67
+ // Set static directory (js and css).
68
+ const staticDir = path.join(__dirname, 'public');
69
+ app.use(express.static(staticDir));
70
+
71
+ // Nav to users pg by default
72
+ app.get('/', (_: Request, res: Response) => {
73
+ return res.redirect('/users');
74
+ });
75
+
76
+ // Redirect to login if not logged in.
77
+ app.get('/users', (_: Request, res: Response) => {
78
+ return res.sendFile('users.html', { root: viewsDir });
79
+ });
80
+
81
+
82
+ /******************************************************************************
83
+ Export default
84
+ ******************************************************************************/
85
+
86
+ export default app;
@@ -0,0 +1,70 @@
1
+ import { RouteError } from '@src/common/util/route-errors';
2
+ import HTTP_STATUS_CODES from '@src/common/constants/HTTP_STATUS_CODES';
3
+
4
+ import UserRepo from '@src/repos/UserRepo';
5
+ import { IUser } from '@src/models/User';
6
+
7
+ /******************************************************************************
8
+ Constants
9
+ ******************************************************************************/
10
+
11
+ export const USER_NOT_FOUND_ERR = 'User not found';
12
+
13
+ /******************************************************************************
14
+ Functions
15
+ ******************************************************************************/
16
+
17
+ /**
18
+ * Get all users.
19
+ */
20
+ function getAll(): Promise<IUser[]> {
21
+ return UserRepo.getAll();
22
+ }
23
+
24
+ /**
25
+ * Add one user.
26
+ */
27
+ function addOne(user: IUser): Promise<void> {
28
+ return UserRepo.add(user);
29
+ }
30
+
31
+ /**
32
+ * Update one user.
33
+ */
34
+ async function updateOne(user: IUser): Promise<void> {
35
+ const persists = await UserRepo.persists(user.id);
36
+ if (!persists) {
37
+ throw new RouteError(
38
+ HTTP_STATUS_CODES.NotFound,
39
+ USER_NOT_FOUND_ERR,
40
+ );
41
+ }
42
+ // Return user
43
+ return UserRepo.update(user);
44
+ }
45
+
46
+ /**
47
+ * Delete a user by their id.
48
+ */
49
+ async function _delete(id: number): Promise<void> {
50
+ const persists = await UserRepo.persists(id);
51
+ if (!persists) {
52
+ throw new RouteError(
53
+ HTTP_STATUS_CODES.NotFound,
54
+ USER_NOT_FOUND_ERR,
55
+ );
56
+ }
57
+ // Delete user
58
+ return UserRepo.delete(id);
59
+ }
60
+
61
+ /******************************************************************************
62
+ Export default
63
+ ******************************************************************************/
64
+
65
+ export default {
66
+ getAll,
67
+ addOne,
68
+ updateOne,
69
+ delete: _delete,
70
+ } as const;