express-generator-typescript 2.9.0 → 3.0.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/README.md +196 -17
- package/lib/template/README.md +33 -15
- package/lib/template/package.json +13 -24
- package/lib/template/src/common/{utils → classes}/route-errors.ts +1 -1
- package/lib/template/src/common/constants/HttpStatusCodes.ts +7 -2
- package/lib/template/src/common/constants/{env.ts → env-inv.ts} +13 -9
- package/lib/template/src/common/utils/dev-only.ts +25 -0
- package/lib/template/src/entities/User/_internal/validators.ts +47 -0
- package/lib/template/src/entities/User/index.ts +2 -0
- package/lib/template/src/entities/User/module.ts +63 -0
- package/lib/template/src/entities/User/types.ts +18 -0
- package/lib/template/src/{models → entities}/common/types.ts +1 -1
- package/lib/template/src/main.ts +1 -1
- package/lib/template/src/public/scripts/HttpClient.js +59 -0
- package/lib/template/src/public/scripts/renderUsers.js +43 -5
- package/lib/template/src/public/scripts/users.js +75 -14
- package/lib/template/src/repos/MockOrm.ts +4 -4
- package/lib/template/src/repos/UserRepo.ts +6 -6
- package/lib/template/src/repos/common/database.json +2 -2
- package/lib/template/src/routes/UserRoutes.ts +2 -2
- package/lib/template/src/routes/common/express-types.ts +1 -1
- package/lib/template/src/routes/common/parseReq.ts +2 -2
- package/lib/template/src/server.ts +17 -5
- package/lib/template/src/services/UserService.ts +6 -6
- package/lib/template/src/views/users.html +1 -1
- package/lib/template/tests/common/comparators.ts +3 -3
- package/lib/template/tests/common/supertest-types.ts +2 -2
- package/lib/template/tests/frontend.test.ts +1 -1
- package/lib/template/tests/support/agent.ts +2 -2
- package/lib/template/tests/users.test.ts +11 -11
- package/lib/template/tsconfig.json +9 -10
- package/lib/template/tsconfig.prod.json +13 -4
- package/lib/template/{vitest.config.mts → vitest.config.ts} +6 -4
- package/package.json +1 -5
- package/lib/template/bs-config.js +0 -8
- package/lib/template/src/models/User.model.ts +0 -95
- package/lib/template/src/public/scripts/http.js +0 -25
package/README.md
CHANGED
|
@@ -1,17 +1,196 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/main/assets/express-typescript.png" width="420">
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
# express-generator-typescript
|
|
6
|
-
|
|
7
|
-
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
8
|
-
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
9
|
-
[](https://github.com/seanpmaxwell/express-generator-typescript/blob/main/LICENSE)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="express-generator-typescript" src="https://github.com/seanpmaxwell/express-generator-typescript/raw/main/assets/express-typescript.png" width="420">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# express-generator-typescript
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
8
|
+
[](https://www.npmjs.com/package/express-generator-typescript)
|
|
9
|
+
[](https://github.com/seanpmaxwell/express-generator-typescript/blob/main/LICENSE)
|
|
10
|
+
|
|
11
|
+
A command-line tool that generates production-ready Express projects with TypeScript built in. Spin up a web server in seconds that follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices).
|
|
12
|
+
|
|
13
|
+
<p align="center">· · ·</p>
|
|
14
|
+
|
|
15
|
+
## 🧭 Overview
|
|
16
|
+
|
|
17
|
+
`express-generator-typescript` works like the classic `express-generator` package, but the project it creates is fully set up for TypeScript. You get strict typing, linting, hot reloading, testing, and production builds, with defaults aimed at APIs. The project is an ES module and comes with an `@src/*` import alias, so imports stay clean as the app grows.
|
|
18
|
+
|
|
19
|
+
<p align="center">· · ·</p>
|
|
20
|
+
|
|
21
|
+
## ✨ Features
|
|
22
|
+
|
|
23
|
+
- **TypeScript-first** – strict compiler settings, linting, and sensible tsconfig defaults, ready to go.
|
|
24
|
+
- **Built for APIs** – ideal for SPAs, mobile backends, or services.
|
|
25
|
+
- **Fast development** – runs TypeScript directly with tsx (no build step), restarts the server when you change it, and refreshes the browser when you change front-end files. Vitest, ESLint, and production builds are included.
|
|
26
|
+
- **Path aliases** – import from `@src/*` anywhere. It works in development, tests, and production builds.
|
|
27
|
+
- **Lean dependencies** – no view engine, ORM, or UI layer; only the essentials for Express + TypeScript.
|
|
28
|
+
|
|
29
|
+
<p align="center">· · ·</p>
|
|
30
|
+
|
|
31
|
+
## 📦 Installation
|
|
32
|
+
|
|
33
|
+
Requires Node.js 22.12 or newer.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx express-generator-typescript
|
|
37
|
+
# or install globally
|
|
38
|
+
npm install -g express-generator-typescript
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
<p align="center">· · ·</p>
|
|
42
|
+
|
|
43
|
+
## ⚡ Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# generate a project (defaults to express-gen-ts)
|
|
47
|
+
npx express-generator-typescript my-api
|
|
48
|
+
|
|
49
|
+
cd my-api
|
|
50
|
+
|
|
51
|
+
# start developing at http://localhost:3000
|
|
52
|
+
npm run dev
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
<p align="center">· · ·</p>
|
|
56
|
+
|
|
57
|
+
## 🖥️ CLI Options
|
|
58
|
+
|
|
59
|
+
| Option | Description |
|
|
60
|
+
| ----------------- | ------------------------------------------------------------------ |
|
|
61
|
+
| `project name` | Folder to create. Defaults to `express-gen-ts`. |
|
|
62
|
+
| `--use-yarn` | Install dependencies with Yarn instead of npm. |
|
|
63
|
+
| `--force` | Write into a folder that isn't empty. Files with the same name are overwritten. |
|
|
64
|
+
| `-h`, `--help` | Show usage. |
|
|
65
|
+
| `-v`, `--version` | Show the generator version. |
|
|
66
|
+
|
|
67
|
+
> Without `--force`, the generator won't write into a folder that already has files in it, so it can't overwrite your work by accident.
|
|
68
|
+
|
|
69
|
+
<p align="center">· · ·</p>
|
|
70
|
+
|
|
71
|
+
## 🧩 Generated Template
|
|
72
|
+
|
|
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
|
+
|
|
75
|
+
### Available `package.json` Scripts
|
|
76
|
+
|
|
77
|
+
- `npm run dev` – Run the server in development with live reload and browser refresh.
|
|
78
|
+
- `npm test` – Run the tests with Vitest.
|
|
79
|
+
- `npm test -- users.test.ts` – Run a single test file.
|
|
80
|
+
- `npm run lint` – Check the code with ESLint.
|
|
81
|
+
- `npm run format` – Format the code with Prettier.
|
|
82
|
+
- `npm run build` – Build the project for production.
|
|
83
|
+
- `npm start` – Run the production build.
|
|
84
|
+
- `npm run typecheck` – Check for TypeScript errors without building.
|
|
85
|
+
- `npm run install:clean` – Delete `node_modules` and the lockfile, then reinstall.
|
|
86
|
+
|
|
87
|
+
### Architecture
|
|
88
|
+
|
|
89
|
+
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
|
+
|
|
91
|
+
Layers explained:
|
|
92
|
+
```yml
|
|
93
|
+
- src/ <-- Source code
|
|
94
|
+
- common/
|
|
95
|
+
- constants/
|
|
96
|
+
- Paths.ts <-- Single source of truth for all API routes
|
|
97
|
+
- routes/ <-- Read and validate values from Express requests; send responses
|
|
98
|
+
- services/ <-- Business logic (where everything comes together)
|
|
99
|
+
- repos/ <-- Talk to the database
|
|
100
|
+
- models/ <-- Describe and handle database records
|
|
101
|
+
- tests/ <-- Tests
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
<p align="center">· · ·</p>
|
|
105
|
+
|
|
106
|
+
## Notes for VS Code users
|
|
107
|
+
|
|
108
|
+
<details>
|
|
109
|
+
<summary>Format on save</summary>
|
|
110
|
+
|
|
111
|
+
The generated project uses ESLint for linting and Prettier for formatting. To format on save, install the Prettier extension for VS Code and set it as the default formatter in `.vscode/settings.json`:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
// .vscode/settings.json
|
|
115
|
+
{
|
|
116
|
+
"editor.minimap.enabled": false,
|
|
117
|
+
"editor.rulers": [80],
|
|
118
|
+
"editor.tabSize": 2,
|
|
119
|
+
|
|
120
|
+
"workbench.sideBar.location": "right",
|
|
121
|
+
"workbench.editor.empty.hint": "hidden",
|
|
122
|
+
|
|
123
|
+
// Formatting: Prettier only
|
|
124
|
+
"editor.formatOnSave": true,
|
|
125
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
126
|
+
|
|
127
|
+
// ESLint: linting only (NO formatting)
|
|
128
|
+
"eslint.format.enable": false,
|
|
129
|
+
"eslint.nodePath": "node_modules",
|
|
130
|
+
"eslint.validate": ["javascript", "typescript", "typescriptreact"],
|
|
131
|
+
|
|
132
|
+
// Run ESLint fixes (non-formatting) on save
|
|
133
|
+
"editor.codeActionsOnSave": {
|
|
134
|
+
"source.fixAll.eslint": "explicit"
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// Language overrides (keep Prettier)
|
|
138
|
+
"[javascript]": {
|
|
139
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
140
|
+
},
|
|
141
|
+
"[typescript]": {
|
|
142
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
143
|
+
},
|
|
144
|
+
"[json]": {
|
|
145
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// JSDoc noise reduction
|
|
149
|
+
"javascript.suggest.completeJSDocs": false,
|
|
150
|
+
"javascript.suggest.jsdoc.generateReturns": false,
|
|
151
|
+
"typescript.suggest.completeJSDocs": false,
|
|
152
|
+
"typescript.suggest.jsdoc.generateReturns": false
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
</details>
|
|
157
|
+
|
|
158
|
+
<details>
|
|
159
|
+
<summary>Debugging</summary>
|
|
160
|
+
|
|
161
|
+
To debug with breakpoints in VS Code, start the app or tests from `.vscode/launch.json`:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
// .vscode/launch.json
|
|
165
|
+
{
|
|
166
|
+
"version": "0.2.0",
|
|
167
|
+
"configurations": [
|
|
168
|
+
{
|
|
169
|
+
"name": "Dev",
|
|
170
|
+
"type": "node",
|
|
171
|
+
"request": "launch",
|
|
172
|
+
"runtimeExecutable": "npm",
|
|
173
|
+
"runtimeArgs": ["run", "dev"],
|
|
174
|
+
"skipFiles": ["<node_internals>/**"],
|
|
175
|
+
"console": "integratedTerminal"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "Test - Vitest",
|
|
179
|
+
"type": "node",
|
|
180
|
+
"request": "launch",
|
|
181
|
+
"runtimeExecutable": "npm",
|
|
182
|
+
"runtimeArgs": ["run", "test"],
|
|
183
|
+
"skipFiles": ["<node_internals>/**"],
|
|
184
|
+
"console": "integratedTerminal"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
</details>
|
|
191
|
+
|
|
192
|
+
<p align="center">· · ·</p>
|
|
193
|
+
|
|
194
|
+
## 📄 License
|
|
195
|
+
|
|
196
|
+
MIT © [seanpmaxwell1](LICENSE)
|
package/lib/template/README.md
CHANGED
|
@@ -2,32 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
This project was created with [express-generator-typescript](https://github.com/seanpmaxwell/express-generator-typescript). It requires Node.js 22.12 or newer.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The original template follows the [TypeScript best practices](https://github.com/seanpmaxwell/Typescript-Best-Practices).
|
|
6
6
|
|
|
7
|
+
<p align="center">· · ·</p>
|
|
7
8
|
|
|
8
9
|
## Available Scripts
|
|
9
10
|
|
|
10
|
-
### `npm run
|
|
11
|
-
|
|
12
|
-
Remove the existing `node_modules/` folder, `package-lock.json`, and reinstall all library modules.
|
|
13
|
-
|
|
14
|
-
### `npm run dev`
|
|
11
|
+
### `npm run dev`
|
|
15
12
|
|
|
16
|
-
Run the server in development
|
|
13
|
+
Run the server in development at http://localhost:3000. The server restarts when you change server code, and the browser refreshes when you change server code or anything in `src/public` or `src/views`.
|
|
17
14
|
|
|
18
|
-
**
|
|
15
|
+
> **Note:** development mode runs your `.ts` files directly with `tsx`, which doesn't check for TypeScript errors. Run `npm run typecheck` to check for them, and let your editor catch most of them as you work.
|
|
19
16
|
|
|
20
17
|
### `npm test`
|
|
21
18
|
|
|
22
|
-
Run
|
|
19
|
+
Run the tests with [Vitest](https://vitest.dev/guide/).
|
|
23
20
|
|
|
24
21
|
### `npm run lint`
|
|
25
22
|
|
|
26
|
-
Check
|
|
23
|
+
Check the code with ESLint.
|
|
27
24
|
|
|
28
25
|
### `npm run format`
|
|
29
26
|
|
|
30
|
-
Format `src/` and `tests/` with
|
|
27
|
+
Format `src/` and `tests/` with Prettier.
|
|
31
28
|
|
|
32
29
|
### `npm run build`
|
|
33
30
|
|
|
@@ -35,16 +32,37 @@ Build the project for production.
|
|
|
35
32
|
|
|
36
33
|
### `npm start`
|
|
37
34
|
|
|
38
|
-
Run the production build
|
|
35
|
+
Run the production build. Run `npm run build` first.
|
|
39
36
|
|
|
40
37
|
### `npm run typecheck`
|
|
41
38
|
|
|
42
|
-
Check for
|
|
39
|
+
Check for TypeScript errors without building.
|
|
40
|
+
|
|
41
|
+
### `npm run install:clean`
|
|
42
|
+
|
|
43
|
+
Delete `node_modules/` and `package-lock.json`, then reinstall all dependencies.
|
|
43
44
|
|
|
44
45
|
<p align="center">· · ·</p>
|
|
45
46
|
|
|
47
|
+
## Tech Stack
|
|
48
|
+
|
|
49
|
+
- **Language**: [TypeScript](https://www.typescriptlang.org/) (strict mode, ES modules)
|
|
50
|
+
- **Web server framework**: [Express](https://expressjs.com/en/) (v5)
|
|
51
|
+
- **Security headers**: [helmet](https://helmet.js.org/) (production only)
|
|
52
|
+
- **Logging**
|
|
53
|
+
- **Request logging**: [morgan](https://github.com/expressjs/morgan) (development only)
|
|
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
|
|
57
|
+
- **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
|
+
- **Testing**: [Vitest](https://vitest.dev) + [Supertest](https://github.com/ladjs/supertest)
|
|
59
|
+
- **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)
|
|
60
|
+
- **Formatting**: [Prettier](https://prettier.io) with [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports)
|
|
61
|
+
- **Building**: `tsc` + [tsc-alias](https://github.com/justkey007/tsc-alias) (rewrites `@src/*` imports in `dist/`)
|
|
62
|
+
|
|
63
|
+
<p align="center">· · ·</p>
|
|
46
64
|
|
|
47
65
|
## Additional Notes
|
|
48
66
|
|
|
49
|
-
- `config/.env.production` is
|
|
50
|
-
- The database is a JSON file
|
|
67
|
+
- `config/.env.production` is in `.gitignore` so production secrets don't get committed. Keep it out of version control and supply its values through your deployment tooling.
|
|
68
|
+
- The database is a JSON file meant only for the demo: `src/repos/common/database.json` in development, or `dist/repos/common/database.json` in production. It's created automatically if missing, but it isn't safe for simultaneous writes. Replace `src/repos/MockOrm.ts` with a real database before going to production.
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-typescript-example",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"scripts": {
|
|
5
|
-
"build": "shx rm -rf dist && npm run lint && tsc --project tsconfig.prod.json && npm run build:copy-static",
|
|
6
|
+
"build": "shx rm -rf dist && npm run lint && tsc --project tsconfig.prod.json && tsc-alias -p tsconfig.prod.json && npm run build:copy-static",
|
|
6
7
|
"build:copy-static": "shx mkdir -p dist/public dist/views && shx cp -r src/public/* dist/public && shx cp -r src/views/* dist/views",
|
|
7
8
|
"install:clean": "shx rm -rf ./node_modules package-lock.json && npm i",
|
|
8
|
-
"dev
|
|
9
|
-
"dev:watch": "nodemon --exec \"npm run dev:basic\" --watch ./src --ext .ts",
|
|
10
|
-
"dev": "concurrently \"npm run dev:watch\" \"npm run sync\"",
|
|
9
|
+
"dev": "cross-env DOTENV_CONFIG_PATH=./config/.env.development tsx watch --import dotenv/config ./src/main.ts",
|
|
11
10
|
"lint": "eslint .",
|
|
12
11
|
"format": "prettier --write .",
|
|
13
|
-
"start": "cross-env DOTENV_CONFIG_PATH=./config/.env.production node
|
|
14
|
-
"sync": "delay 1s && browser-sync start --config bs-config.js",
|
|
12
|
+
"start": "cross-env DOTENV_CONFIG_PATH=./config/.env.production node --import dotenv/config ./dist/main.js",
|
|
15
13
|
"test": "cross-env NODE_ENV=test vitest",
|
|
16
14
|
"typecheck": "tsc -b --noEmit"
|
|
17
15
|
},
|
|
18
16
|
"engines": {
|
|
19
17
|
"node": ">=22.12.0"
|
|
20
18
|
},
|
|
21
|
-
"_moduleAliases": {
|
|
22
|
-
"@src": "dist"
|
|
23
|
-
},
|
|
24
19
|
"dependencies": {
|
|
25
20
|
"cross-env": "^10.1.0",
|
|
26
21
|
"dotenv": "^18.0.4",
|
|
@@ -29,40 +24,34 @@
|
|
|
29
24
|
"jet-env": "^1.1.6",
|
|
30
25
|
"jet-id": "^1.3.1",
|
|
31
26
|
"jet-logger": "3.0.0",
|
|
32
|
-
"jet-paths": "^4.0.
|
|
27
|
+
"jet-paths": "^4.0.2",
|
|
33
28
|
"jet-validators": "^2.3.1",
|
|
34
29
|
"jsonfile": "^6.2.1",
|
|
35
|
-
"
|
|
36
|
-
"morgan": "^1.12.1",
|
|
37
|
-
"tspo": "^1.0.7"
|
|
30
|
+
"morgan": "^1.12.1"
|
|
38
31
|
},
|
|
39
32
|
"devDependencies": {
|
|
40
33
|
"@eslint/js": "^10.0.1",
|
|
41
|
-
"@swc/core": "^1.16.2",
|
|
42
34
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
35
|
+
"@types/connect-livereload": "^0.6.3",
|
|
43
36
|
"@types/express": "^5.0.6",
|
|
44
37
|
"@types/jsonfile": "^6.1.4",
|
|
38
|
+
"@types/livereload": "^0.9.5",
|
|
45
39
|
"@types/morgan": "^1.9.10",
|
|
46
40
|
"@types/node": "^26.6.2",
|
|
47
41
|
"@types/supertest": "^7.2.1",
|
|
48
|
-
"
|
|
49
|
-
"concurrently": "^10.0.5",
|
|
50
|
-
"delay-cli": "^3.0.0",
|
|
42
|
+
"connect-livereload": "^0.6.1",
|
|
51
43
|
"eslint": "^10.11.0",
|
|
52
44
|
"eslint-config-prettier": "^10.1.8",
|
|
53
45
|
"eslint-plugin-n": "^18.3.0",
|
|
54
46
|
"jiti": "^2.7.0",
|
|
55
|
-
"
|
|
47
|
+
"livereload": "^0.10.3",
|
|
56
48
|
"prettier": "^3.9.9",
|
|
57
49
|
"shx": "^0.4.0",
|
|
58
50
|
"supertest": "^7.3.0",
|
|
59
|
-
"
|
|
60
|
-
"
|
|
51
|
+
"tsc-alias": "^1.9.5",
|
|
52
|
+
"tsx": "^4.23.15",
|
|
61
53
|
"typescript": "^6.0.3",
|
|
62
54
|
"typescript-eslint": "^8.70.1",
|
|
63
55
|
"vitest": "^5.0.2"
|
|
64
|
-
},
|
|
65
|
-
"allowScripts": {
|
|
66
|
-
"@swc/core@1.16.2": true
|
|
67
56
|
}
|
|
68
57
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ValueOf } from '../types/utility-types';
|
|
1
|
+
import type { ValueOf } from '../types/utility-types';
|
|
2
2
|
|
|
3
3
|
// ========================================================================= //
|
|
4
4
|
// CONSTANTS //
|
|
@@ -386,8 +386,13 @@ const HttpStatusCodes = {
|
|
|
386
386
|
} as const;
|
|
387
387
|
|
|
388
388
|
// ========================================================================= //
|
|
389
|
-
//
|
|
389
|
+
// TYPES //
|
|
390
390
|
// ========================================================================= //
|
|
391
391
|
|
|
392
392
|
type HttpStatusCodes = ValueOf<typeof HttpStatusCodes>;
|
|
393
|
+
|
|
394
|
+
// ========================================================================= //
|
|
395
|
+
// EXPORT //
|
|
396
|
+
// ========================================================================= //
|
|
397
|
+
|
|
393
398
|
export default HttpStatusCodes;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import jetEnv, { num } from 'jet-env';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type { ValueOf } from '../types/utility-types';
|
|
3
4
|
|
|
4
5
|
// ========================================================================= //
|
|
5
6
|
// CONSTANTS //
|
|
@@ -11,18 +12,21 @@ export const NodeEnvs = {
|
|
|
11
12
|
TEST: 'test',
|
|
12
13
|
PRODUCTION: 'production',
|
|
13
14
|
} as const;
|
|
15
|
+
export type NodeEnvs = ValueOf<typeof NodeEnvs>;
|
|
14
16
|
|
|
15
17
|
// ========================================================================= //
|
|
16
18
|
// EXEC //
|
|
17
19
|
// ========================================================================= //
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// Setup the is `NodeEnvs` validator
|
|
22
|
+
const isNodeEnv = (() => {
|
|
23
|
+
const vals = Object.values(NodeEnvs);
|
|
24
|
+
const valsFin = vals.map((item) => item.toLowerCase());
|
|
25
|
+
const set = new Set(valsFin);
|
|
26
|
+
return (val: unknown): val is NodeEnvs => set.has(val as NodeEnvs);
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
export const EnvVars = jetEnv({
|
|
30
|
+
NodeEnv: isNodeEnv,
|
|
21
31
|
Port: num,
|
|
22
32
|
});
|
|
23
|
-
|
|
24
|
-
// ========================================================================= //
|
|
25
|
-
// EXPORT //
|
|
26
|
-
// ========================================================================= //
|
|
27
|
-
|
|
28
|
-
export default EnvVars;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import connectLiveReload from 'connect-livereload';
|
|
2
|
+
import type { Express } from 'express';
|
|
3
|
+
import livereload from 'livereload';
|
|
4
|
+
|
|
5
|
+
// ========================================================================= //
|
|
6
|
+
// FUNCTIONS //
|
|
7
|
+
// ========================================================================= //
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Development only: refresh the browser when front-end files change, and
|
|
11
|
+
* after `tsx watch` restarts the server for a TypeScript change.
|
|
12
|
+
*
|
|
13
|
+
* Must be registered before the routes that serve html so the reload script
|
|
14
|
+
* gets injected into those pages.
|
|
15
|
+
*/
|
|
16
|
+
export function setupLiveReload(app: Express, watchDirs: string[]): void {
|
|
17
|
+
const server = livereload.createServer();
|
|
18
|
+
server.watch(watchDirs);
|
|
19
|
+
// A new process means the backend just restarted; reload once the browser
|
|
20
|
+
// reconnects so it picks up the new server code.
|
|
21
|
+
server.server.once('connection', () => {
|
|
22
|
+
setTimeout(() => server.refresh('/'), 100);
|
|
23
|
+
});
|
|
24
|
+
app.use(connectLiveReload());
|
|
25
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import jetid from 'jet-id';
|
|
2
|
+
import { isNonEmptyString, isString } from 'jet-validators';
|
|
3
|
+
import { parseObject, type Schema, testObject } from 'jet-validators/utils';
|
|
4
|
+
|
|
5
|
+
import { isISOString } from '@src/common/utils/date-utils';
|
|
6
|
+
|
|
7
|
+
import type { UserEntity, UserInput } from '../types';
|
|
8
|
+
|
|
9
|
+
// ========================================================================= //
|
|
10
|
+
// FUNCTIONS //
|
|
11
|
+
// ========================================================================= //
|
|
12
|
+
|
|
13
|
+
const schema: Schema<UserEntity> = {
|
|
14
|
+
id: isUserId,
|
|
15
|
+
name: isString,
|
|
16
|
+
email: isString,
|
|
17
|
+
created: isISOString,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validate the `User` schema.
|
|
22
|
+
*/
|
|
23
|
+
export const parseUser = parseObject<UserEntity>(schema);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* For the APIs make sure the right fields are complete
|
|
27
|
+
*/
|
|
28
|
+
export const isCompleteUser = testObject<UserEntity>({
|
|
29
|
+
...schema,
|
|
30
|
+
name: isNonEmptyString,
|
|
31
|
+
email: isNonEmptyString,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate the fields a client sends to create a user.
|
|
36
|
+
*/
|
|
37
|
+
export const isUserInput = testObject<UserInput>({
|
|
38
|
+
name: isNonEmptyString,
|
|
39
|
+
email: isNonEmptyString,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Test if an id is a valid user id.
|
|
44
|
+
*/
|
|
45
|
+
export function isUserId(val: unknown): val is string {
|
|
46
|
+
return jetid.test(val);
|
|
47
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import jetid from 'jet-id';
|
|
2
|
+
|
|
3
|
+
import { getISOString, type ISOString } from '@src/common/utils/date-utils';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
isCompleteUser,
|
|
7
|
+
isUserId,
|
|
8
|
+
isUserInput,
|
|
9
|
+
parseUser,
|
|
10
|
+
} from './_internal/validators';
|
|
11
|
+
import type { UserEntity } from './types';
|
|
12
|
+
|
|
13
|
+
// ========================================================================= //
|
|
14
|
+
// FUNCTIONS //
|
|
15
|
+
// ========================================================================= //
|
|
16
|
+
|
|
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
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Factory-function.
|
|
31
|
+
*
|
|
32
|
+
* Create a `UserEntity` from a partial or `undefined`
|
|
33
|
+
*/
|
|
34
|
+
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
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Factory-function.
|
|
42
|
+
*
|
|
43
|
+
* Create a `UserEntity` from individual properties
|
|
44
|
+
*/
|
|
45
|
+
function of(name: string, email?: string, created?: ISOString): UserEntity {
|
|
46
|
+
const retVal = getDefaults();
|
|
47
|
+
if (name) retVal.name = name;
|
|
48
|
+
if (email) retVal.email = email;
|
|
49
|
+
if (created) retVal.created = created;
|
|
50
|
+
return retVal;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ========================================================================= //
|
|
54
|
+
// EXPORT //
|
|
55
|
+
// ========================================================================= //
|
|
56
|
+
|
|
57
|
+
export default {
|
|
58
|
+
of,
|
|
59
|
+
create,
|
|
60
|
+
isId: isUserId,
|
|
61
|
+
isComplete: isCompleteUser,
|
|
62
|
+
isInput: isUserInput,
|
|
63
|
+
} as const;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Entity } from '@src/entities/common/types';
|
|
2
|
+
|
|
3
|
+
// ========================================================================= //
|
|
4
|
+
// TYPES //
|
|
5
|
+
// ========================================================================= //
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @entity `users`
|
|
9
|
+
*/
|
|
10
|
+
export interface UserEntity extends Entity {
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fields a client supplies when creating a user; the server sets the rest.
|
|
17
|
+
*/
|
|
18
|
+
export type UserInput = Pick<UserEntity, 'name' | 'email'>;
|
package/lib/template/src/main.ts
CHANGED