express-generator-typescript 3.0.4 → 3.2.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.
- package/README.md +10 -11
- package/bin/cli.js +1 -20
- package/lib/template/.prettierrc.json +1 -1
- package/lib/template/README.md +13 -15
- package/lib/template/eslint.config.ts +10 -1
- package/lib/template/gitignore +0 -1
- package/lib/template/package.json +19 -29
- package/lib/template/src/common/classes/route-errors.ts +1 -14
- package/lib/template/src/common/constants/HttpStatusCodes.ts +1 -1
- package/lib/template/src/common/constants/Paths/Paths.ts +29 -0
- package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
- package/lib/template/src/common/constants/Paths/index.ts +1 -0
- package/lib/template/src/common/constants/{environment-consts.ts → env.constants.ts} +13 -15
- package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
- package/lib/template/src/common/utils/dev-only.ts +1 -1
- package/lib/template/src/common/utils/validators.ts +0 -23
- package/lib/template/src/entities/User/index.ts +2 -2
- package/lib/template/src/entities/User/module.ts +77 -28
- package/lib/template/src/entities/User/types.ts +1 -1
- package/lib/template/src/entities/common/entity-base-types.ts +1 -1
- package/lib/template/src/main.ts +4 -4
- package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
- package/lib/template/src/public/scripts/users.js +19 -10
- package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
- package/lib/template/src/public/stylesheets/users.css +24 -1
- package/lib/template/src/repos/MockOrm.ts +6 -6
- package/lib/template/src/repos/UserRepo.ts +6 -16
- package/lib/template/src/repos/common/database.json +3 -3
- package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +4 -12
- package/lib/template/src/routers/user.router.ts +65 -0
- package/lib/template/src/server.ts +24 -11
- package/lib/template/src/services/UserService.ts +6 -3
- package/lib/template/src/views/users.html +8 -0
- package/lib/template/tests/frontend.test.ts +28 -17
- package/lib/template/tests/support/agent.ts +4 -4
- package/lib/template/tests/support/utils.ts +23 -0
- package/lib/template/tests/users.test.ts +136 -83
- package/lib/template/tsconfig.json +10 -13
- package/package.json +6 -6
- package/lib/template/src/common/constants/Paths.ts +0 -16
- package/lib/template/src/entities/User/_internal/validators.ts +0 -47
- package/lib/template/src/routes/UserRoutes.ts +0 -74
- package/lib/template/src/routes/common/express-types.ts +0 -9
- package/lib/template/src/routes/common/parseReq.ts +0 -23
- package/lib/template/tests/common/comparators.ts +0 -36
- package/lib/template/tests/common/supertest-types.ts +0 -11
- package/lib/template/tsconfig.prod.json +0 -30
- package/lib/template/vitest.config.ts +0 -25
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ A command-line tool that generates production-ready Express projects with TypeSc
|
|
|
14
14
|
|
|
15
15
|
## 🧭 Overview
|
|
16
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,
|
|
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, and testing, with defaults aimed at APIs and no build step. The project is an ES module and comes with a `#src/*` import alias, so imports stay clean as the app grows.
|
|
18
18
|
|
|
19
19
|
<p align="center">· · ·</p>
|
|
20
20
|
|
|
@@ -22,15 +22,15 @@ A command-line tool that generates production-ready Express projects with TypeSc
|
|
|
22
22
|
|
|
23
23
|
- **TypeScript-first** – strict compiler settings, linting, and sensible tsconfig defaults, ready to go.
|
|
24
24
|
- **Built for APIs** – ideal for SPAs, mobile backends, or services.
|
|
25
|
-
- **Fast development** – runs TypeScript directly with
|
|
26
|
-
- **Path aliases** – import from
|
|
25
|
+
- **Fast development** – runs TypeScript directly with Node's built-in type stripping (no build step, in development or production), restarts the server when you change it, and refreshes the browser when you change front-end files. Node's built-in test runner and ESLint are included.
|
|
26
|
+
- **Path aliases** – import from `#src/*` anywhere. It works in development, tests, and production.
|
|
27
27
|
- **Lean dependencies** – no view engine, ORM, or UI layer; only the essentials for Express + TypeScript.
|
|
28
28
|
|
|
29
29
|
<p align="center">· · ·</p>
|
|
30
30
|
|
|
31
31
|
## 📦 Installation
|
|
32
32
|
|
|
33
|
-
Requires Node.js 22.
|
|
33
|
+
Requires Node.js 22.18+ (22.x), 24.x, or 26+.
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
npx express-generator-typescript
|
|
@@ -70,20 +70,19 @@ npm run dev
|
|
|
70
70
|
|
|
71
71
|
## 🧩 Generated Template
|
|
72
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,
|
|
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, testing, and hot reloading are all set up for you.
|
|
74
74
|
|
|
75
75
|
---
|
|
76
76
|
|
|
77
77
|
#### Available `package.json` scripts
|
|
78
78
|
|
|
79
79
|
- `npm run dev` – Run the server in development with live reload and browser refresh.
|
|
80
|
-
- `npm test` – Run the tests with
|
|
81
|
-
- `npm test -- users.test.ts` – Run a single test file.
|
|
80
|
+
- `npm test` – Run the tests with Node's built-in test runner.
|
|
81
|
+
- `npm test -- tests/users.test.ts` – Run a single test file.
|
|
82
82
|
- `npm run lint` – Check the code with ESLint.
|
|
83
83
|
- `npm run format` – Format the code with Prettier.
|
|
84
|
-
- `npm
|
|
85
|
-
- `npm
|
|
86
|
-
- `npm run typecheck` – Check for TypeScript errors without building.
|
|
84
|
+
- `npm start` – Run the server in production mode (no build step).
|
|
85
|
+
- `npm run typecheck` – Check for TypeScript errors.
|
|
87
86
|
- `npm run install:clean` – Delete `node_modules` and the lockfile, then reinstall.
|
|
88
87
|
|
|
89
88
|
---
|
|
@@ -187,7 +186,7 @@ To debug with breakpoints in VS Code, start the app or tests from `.vscode/launc
|
|
|
187
186
|
"console": "integratedTerminal"
|
|
188
187
|
},
|
|
189
188
|
{
|
|
190
|
-
"name": "Test
|
|
189
|
+
"name": "Test",
|
|
191
190
|
"type": "node",
|
|
192
191
|
"request": "launch",
|
|
193
192
|
"runtimeExecutable": "npm",
|
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
|
|
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
|
-
}
|
package/lib/template/README.md
CHANGED
|
@@ -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.
|
|
3
|
+
This project was created with [express-generator-typescript](https://github.com/seanpmaxwell/express-generator-typescript). It requires Node.js 22.18+ (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
|
|
|
@@ -12,11 +12,11 @@ The original template follows the [TypeScript best practices](https://github.com
|
|
|
12
12
|
|
|
13
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`.
|
|
14
14
|
|
|
15
|
-
> **Note:**
|
|
15
|
+
> **Note:** Node runs your `.ts` files directly with Node's built-in [type stripping](https://nodejs.org/api/typescript.html), 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.
|
|
16
16
|
|
|
17
17
|
### `npm test`
|
|
18
18
|
|
|
19
|
-
Run the tests with [
|
|
19
|
+
Run the tests with Node's built-in [test runner](https://nodejs.org/api/test.html). Pass a file to run only that file (`npm test -- tests/users.test.ts`), or `--test-name-pattern` to run only matching tests (`npm test -- --test-name-pattern="UserRouter"`).
|
|
20
20
|
|
|
21
21
|
### `npm run lint`
|
|
22
22
|
|
|
@@ -26,17 +26,13 @@ Check the code with ESLint.
|
|
|
26
26
|
|
|
27
27
|
Format `src/` and `tests/` with Prettier.
|
|
28
28
|
|
|
29
|
-
### `npm run build`
|
|
30
|
-
|
|
31
|
-
Build the project for production.
|
|
32
|
-
|
|
33
29
|
### `npm start`
|
|
34
30
|
|
|
35
|
-
Run the production
|
|
31
|
+
Run the server in production mode. There's no build step: like `npm run dev`, this runs the `.ts` files in `src/` directly, so run `npm run typecheck` and `npm run lint` before you deploy.
|
|
36
32
|
|
|
37
33
|
### `npm run typecheck`
|
|
38
34
|
|
|
39
|
-
Check for TypeScript errors
|
|
35
|
+
Check for TypeScript errors.
|
|
40
36
|
|
|
41
37
|
### `npm run install:clean`
|
|
42
38
|
|
|
@@ -52,17 +48,19 @@ Delete `node_modules/` and `package-lock.json`, then reinstall all dependencies.
|
|
|
52
48
|
- **Logging**
|
|
53
49
|
- **Request logging**: [morgan](https://github.com/expressjs/morgan) (development only)
|
|
54
50
|
- **General logging**: [jet-logger](https://github.com/seanpmaxwell/jet-logger)
|
|
55
|
-
- **Validation**: [
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
58
|
-
- **
|
|
51
|
+
- **Validation**: [zod](https://github.com/colinhacks/zod)
|
|
52
|
+
- **Middleware Validation**: [express-zod-safe](https://github.com/AngaBlue/express-zod-safe)
|
|
53
|
+
- **Environment variables**: Node's built-in [`--env-file`](https://nodejs.org/api/cli.html#--env-fileconfig) loads `config/.env.*`
|
|
54
|
+
- **Reloading**: Node's built-in [`--watch`](https://nodejs.org/api/cli.html#--watch) (restarts the server) and [livereload](https://github.com/napcs/node-livereload) + [connect-livereload](https://github.com/intesso/connect-livereload) (refreshes the browser)
|
|
55
|
+
- **Testing**: Node's built-in [`node:test`](https://nodejs.org/api/test.html) + [Supertest](https://github.com/ladjs/supertest)
|
|
59
56
|
- **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
57
|
- **Formatting**: [Prettier](https://prettier.io) with [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports)
|
|
61
|
-
- **
|
|
58
|
+
- **Type checking**: `tsc` (`npm run typecheck`). There's no build step.
|
|
62
59
|
|
|
63
60
|
<p align="center">· · ·</p>
|
|
64
61
|
|
|
65
62
|
## Additional Notes
|
|
66
63
|
|
|
64
|
+
- Imports name the `.ts` file (`./server.ts`), because Node runs the source files directly. `#src/*` imports are [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) set up in `package.json` that point at `src/*`.
|
|
67
65
|
- `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
|
|
66
|
+
- The database is a JSON file meant only for the demo: `src/repos/common/database.json`. 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.
|
|
@@ -10,7 +10,7 @@ import tseslint from 'typescript-eslint';
|
|
|
10
10
|
|
|
11
11
|
export default defineConfig([
|
|
12
12
|
// global ignores
|
|
13
|
-
globalIgnores(['**/
|
|
13
|
+
globalIgnores(['**/public/**/lib/**']),
|
|
14
14
|
// linting rules (code quality only)
|
|
15
15
|
{
|
|
16
16
|
files: ['**/*.{ts,tsx}'],
|
|
@@ -38,6 +38,15 @@ export default defineConfig([
|
|
|
38
38
|
ignoreRestSiblings: true,
|
|
39
39
|
},
|
|
40
40
|
],
|
|
41
|
+
// node:test's `describe`/`it` return promises the runner already tracks
|
|
42
|
+
'@typescript-eslint/no-floating-promises': [
|
|
43
|
+
'error',
|
|
44
|
+
{
|
|
45
|
+
allowForKnownSafeCalls: [
|
|
46
|
+
{ from: 'package', name: ['describe', 'it'], package: 'node:test' },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
41
50
|
'no-console': 'warn',
|
|
42
51
|
'no-extra-boolean-cast': 'off',
|
|
43
52
|
'no-process-env': 'warn',
|
package/lib/template/gitignore
CHANGED
|
@@ -3,57 +3,47 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"install:clean": "shx rm -rf ./node_modules package-lock.json && npm i",
|
|
9
|
-
"dev": "cross-env DOTENV_CONFIG_PATH=./config/.env.development tsx watch --import dotenv/config ./src/main.ts",
|
|
6
|
+
"install:clean": "node -e \"fs.rmSync('node_modules', { recursive: true, force: true }); fs.rmSync('package-lock.json', { force: true })\" && npm i",
|
|
7
|
+
"dev": "node --env-file=config/.env.development --watch src/main.ts",
|
|
10
8
|
"lint": "eslint .",
|
|
11
9
|
"format": "prettier --write .",
|
|
12
|
-
"start": "
|
|
13
|
-
"test": "
|
|
10
|
+
"start": "node --env-file-if-exists=config/.env.production src/main.ts",
|
|
11
|
+
"test": "node --env-file=config/.env.test --test --test-concurrency=1",
|
|
14
12
|
"typecheck": "tsc -b --noEmit"
|
|
15
13
|
},
|
|
14
|
+
"imports": {
|
|
15
|
+
"#src/*": "./src/*"
|
|
16
|
+
},
|
|
16
17
|
"engines": {
|
|
17
|
-
"node": "
|
|
18
|
+
"node": "^22.18.0 || ^24.0.0 || >=26.0.0"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"express": "^5.2.1",
|
|
21
|
+
"express": "^5.3.0",
|
|
22
|
+
"express-zod-safe": "^3.2.1",
|
|
23
23
|
"helmet": "^8.3.0",
|
|
24
|
-
"jet-id": "^1.3.1",
|
|
25
24
|
"jet-logger": "3.0.0",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"morgan": "^1.12.1"
|
|
25
|
+
"morgan": "^1.12.1",
|
|
26
|
+
"uuid": "^14.0.3",
|
|
27
|
+
"zod": "^4.6.5"
|
|
30
28
|
},
|
|
31
29
|
"devDependencies": {
|
|
32
30
|
"@eslint/js": "^10.0.1",
|
|
33
31
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
34
32
|
"@types/connect-livereload": "^0.6.3",
|
|
35
33
|
"@types/express": "^5.0.6",
|
|
36
|
-
"@types/jsonfile": "^6.1.4",
|
|
37
34
|
"@types/livereload": "^0.9.5",
|
|
38
35
|
"@types/morgan": "^1.9.10",
|
|
39
|
-
"@types/node": "^26.6.
|
|
36
|
+
"@types/node": "^26.6.5",
|
|
40
37
|
"@types/supertest": "^7.2.1",
|
|
41
38
|
"connect-livereload": "^0.6.1",
|
|
42
|
-
"eslint": "^10.
|
|
39
|
+
"eslint": "^10.12.0",
|
|
43
40
|
"eslint-config-prettier": "^10.1.8",
|
|
44
|
-
"eslint-plugin-n": "^18.
|
|
41
|
+
"eslint-plugin-n": "^18.4.1",
|
|
45
42
|
"jiti": "^2.7.0",
|
|
46
43
|
"livereload": "^0.10.3",
|
|
47
|
-
"prettier": "^3.9.
|
|
48
|
-
"
|
|
49
|
-
"supertest": "^7.3.0",
|
|
50
|
-
"tsc-alias": "^1.9.5",
|
|
51
|
-
"tsx": "^4.23.15",
|
|
44
|
+
"prettier": "^3.9.10",
|
|
45
|
+
"supertest": "^7.3.1",
|
|
52
46
|
"typescript": "^6.0.3",
|
|
53
|
-
"typescript-eslint": "^8.
|
|
54
|
-
"vitest": "^5.0.2"
|
|
55
|
-
},
|
|
56
|
-
"allowScripts": {
|
|
57
|
-
"esbuild@0.28.2": true
|
|
47
|
+
"typescript-eslint": "^8.71.1"
|
|
58
48
|
}
|
|
59
49
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import HttpStatusCodes from '@src/common/constants/HttpStatusCodes';
|
|
1
|
+
import HttpStatusCodes from '#src/common/constants/HttpStatusCodes.ts';
|
|
4
2
|
|
|
5
3
|
// ========================================================================= //
|
|
6
4
|
// CLASSES //
|
|
@@ -33,14 +31,3 @@ export class NotFoundError extends RouteError {
|
|
|
33
31
|
super(HttpStatusCodes.NOT_FOUND, message);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Request data failed schema validation.
|
|
39
|
-
*/
|
|
40
|
-
export class ValidationError extends RouteError {
|
|
41
|
-
public static MESSAGE = 'Request validation failed.';
|
|
42
|
-
|
|
43
|
-
public constructor(errors: ParseError[]) {
|
|
44
|
-
super(HttpStatusCodes.BAD_REQUEST, ValidationError.MESSAGE, errors);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ep, pp } from './helpers.ts';
|
|
2
|
+
|
|
3
|
+
// ========================================================================= //
|
|
4
|
+
// CONSTANTS //
|
|
5
|
+
// ========================================================================= //
|
|
6
|
+
|
|
7
|
+
const BASE = '/api';
|
|
8
|
+
const USERS_BASE = '/users';
|
|
9
|
+
const USERS = BASE + USERS_BASE;
|
|
10
|
+
|
|
11
|
+
const Paths = {
|
|
12
|
+
PATH: BASE,
|
|
13
|
+
Users: {
|
|
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
|
+
},
|
|
22
|
+
},
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
// ========================================================================= //
|
|
26
|
+
// EXPORT //
|
|
27
|
+
// ========================================================================= //
|
|
28
|
+
|
|
29
|
+
export default Paths;
|
|
@@ -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.ts';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-process-env */
|
|
2
2
|
import logger from 'jet-logger';
|
|
3
|
-
|
|
4
|
-
import { isNumber, isValueOf, type ValueOf } from '../utils/validators';
|
|
3
|
+
import { z } from 'zod';
|
|
5
4
|
|
|
6
5
|
// ========================================================================= //
|
|
7
6
|
// CONSTANTS //
|
|
@@ -13,9 +12,9 @@ export const NodeEnvs = {
|
|
|
13
12
|
TEST: 'test',
|
|
14
13
|
PRODUCTION: 'production',
|
|
15
14
|
} as const;
|
|
16
|
-
export type NodeEnvs =
|
|
15
|
+
export type NodeEnvs = z.infer<typeof NodeEnvsEnum>;
|
|
17
16
|
|
|
18
|
-
export const
|
|
17
|
+
export const Env = {
|
|
19
18
|
NODE_ENV: process.env.NODE_ENV?.toLowerCase(),
|
|
20
19
|
PORT: Number(process.env.PORT),
|
|
21
20
|
} as const;
|
|
@@ -24,19 +23,18 @@ export const EnvVars = {
|
|
|
24
23
|
// EXEC //
|
|
25
24
|
// ========================================================================= //
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
|
|
29
35
|
{
|
|
30
36
|
try {
|
|
31
|
-
|
|
32
|
-
if (!isNodeEnv(EnvVars.NODE_ENV)) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
'process.env.NODE_ENV must be "development", "test", or "production"',
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
if (!isNumber(EnvVars.PORT)) {
|
|
38
|
-
throw new Error('process.env.PORT must be a valid number');
|
|
39
|
-
}
|
|
37
|
+
EnvSchema.parse(Env);
|
|
40
38
|
} catch (err) {
|
|
41
39
|
logger.err(err);
|
|
42
40
|
throw err;
|
|
@@ -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
|
-
}
|
|
@@ -8,7 +8,7 @@ import livereload from 'livereload';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Development only: refresh the browser when front-end files change, and
|
|
11
|
-
* after `
|
|
11
|
+
* after `node --watch` restarts the server for a TypeScript change.
|
|
12
12
|
*
|
|
13
13
|
* Must be registered before the routes that serve html so the reload script
|
|
14
14
|
* gets injected into those pages.
|
|
@@ -3,26 +3,3 @@
|
|
|
3
3
|
// ========================================================================= //
|
|
4
4
|
|
|
5
5
|
export type ValueOf<T> = T[keyof T];
|
|
6
|
-
type Table = Record<string, string> | Record<string, number>;
|
|
7
|
-
|
|
8
|
-
// ========================================================================= //
|
|
9
|
-
// EXEC //
|
|
10
|
-
// ========================================================================= //
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if an `unknown` is a number type.
|
|
14
|
-
*/
|
|
15
|
-
export function isNumber<T>(val: T): val is Extract<T, number> {
|
|
16
|
-
return typeof val === 'number' && !isNaN(val);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Check if an `unknown` is a value of an object.
|
|
21
|
-
*/
|
|
22
|
-
export function isValueOf<T extends Table>(
|
|
23
|
-
table: T,
|
|
24
|
-
): (v: unknown) => v is ValueOf<T> {
|
|
25
|
-
const vals = Object.values(table);
|
|
26
|
-
const set = new Set(vals);
|
|
27
|
-
return (val: unknown): val is ValueOf<T> => set.has(val);
|
|
28
|
-
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as default } from './module';
|
|
2
|
-
export type { UserEntity, UserInput } from './types';
|
|
1
|
+
export { default as default } from './module.ts';
|
|
2
|
+
export type { UserEntity, UserInput } from './types.ts';
|