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.
Files changed (48) hide show
  1. package/README.md +10 -11
  2. package/bin/cli.js +1 -20
  3. package/lib/template/.prettierrc.json +1 -1
  4. package/lib/template/README.md +13 -15
  5. package/lib/template/eslint.config.ts +10 -1
  6. package/lib/template/gitignore +0 -1
  7. package/lib/template/package.json +19 -29
  8. package/lib/template/src/common/classes/route-errors.ts +1 -14
  9. package/lib/template/src/common/constants/HttpStatusCodes.ts +1 -1
  10. package/lib/template/src/common/constants/Paths/Paths.ts +29 -0
  11. package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
  12. package/lib/template/src/common/constants/Paths/index.ts +1 -0
  13. package/lib/template/src/common/constants/{environment-consts.ts → env.constants.ts} +13 -15
  14. package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
  15. package/lib/template/src/common/utils/dev-only.ts +1 -1
  16. package/lib/template/src/common/utils/validators.ts +0 -23
  17. package/lib/template/src/entities/User/index.ts +2 -2
  18. package/lib/template/src/entities/User/module.ts +77 -28
  19. package/lib/template/src/entities/User/types.ts +1 -1
  20. package/lib/template/src/entities/common/entity-base-types.ts +1 -1
  21. package/lib/template/src/main.ts +4 -4
  22. package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
  23. package/lib/template/src/public/scripts/users.js +19 -10
  24. package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
  25. package/lib/template/src/public/stylesheets/users.css +24 -1
  26. package/lib/template/src/repos/MockOrm.ts +6 -6
  27. package/lib/template/src/repos/UserRepo.ts +6 -16
  28. package/lib/template/src/repos/common/database.json +3 -3
  29. package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +4 -12
  30. package/lib/template/src/routers/user.router.ts +65 -0
  31. package/lib/template/src/server.ts +24 -11
  32. package/lib/template/src/services/UserService.ts +6 -3
  33. package/lib/template/src/views/users.html +8 -0
  34. package/lib/template/tests/frontend.test.ts +28 -17
  35. package/lib/template/tests/support/agent.ts +4 -4
  36. package/lib/template/tests/support/utils.ts +23 -0
  37. package/lib/template/tests/users.test.ts +136 -83
  38. package/lib/template/tsconfig.json +10 -13
  39. package/package.json +6 -6
  40. package/lib/template/src/common/constants/Paths.ts +0 -16
  41. package/lib/template/src/entities/User/_internal/validators.ts +0 -47
  42. package/lib/template/src/routes/UserRoutes.ts +0 -74
  43. package/lib/template/src/routes/common/express-types.ts +0 -9
  44. package/lib/template/src/routes/common/parseReq.ts +0 -23
  45. package/lib/template/tests/common/comparators.ts +0 -36
  46. package/lib/template/tests/common/supertest-types.ts +0 -11
  47. package/lib/template/tsconfig.prod.json +0 -30
  48. 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, 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.
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 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.
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.12 or newer.
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, building, and hot reloading are all set up for you.
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 Vitest.
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 run build` – Build the project for production.
85
- - `npm start` – Run the production build.
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 - Vitest",
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, 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
- }
@@ -2,7 +2,7 @@
2
2
  "plugins": ["@trivago/prettier-plugin-sort-imports"],
3
3
  "importOrder": [
4
4
  "<THIRD_PARTY_MODULES>",
5
- "^@src/[^/]+(/.*)?$",
5
+ "^#src/[^/]+(/.*)?$",
6
6
  "^\\.(?!.*\\.css$).*$",
7
7
  "^.+\\.css$"
8
8
  ],
@@ -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.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:** 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.
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 [Vitest](https://vitest.dev/guide/).
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 build. Run `npm run build` first.
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 without building.
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**: [jet-validators](https://github.com/seanpmaxwell/jet-validators)
56
- - **Environment variables**: [dotenv](https://github.com/motdotla/dotenv) loads `config/.env.*`
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)
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
- - **Building**: `tsc` + [tsc-alias](https://github.com/justkey007/tsc-alias) (rewrites `@src/*` imports in `dist/`)
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` 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.
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(['**/dist/**', '**/public/**/lib/**']),
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',
@@ -1,5 +1,4 @@
1
1
  **/node_modules
2
- dist/
3
2
  **/**/*.log
4
3
  tsconfig.tsbuildinfo
5
4
 
@@ -3,57 +3,47 @@
3
3
  "version": "1.0.0",
4
4
  "type": "module",
5
5
  "scripts": {
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",
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",
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": "cross-env DOTENV_CONFIG_PATH=./config/.env.production node --import dotenv/config ./dist/main.js",
13
- "test": "cross-env NODE_ENV=test vitest",
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": ">=22.12.0"
18
+ "node": "^22.18.0 || ^24.0.0 || >=26.0.0"
18
19
  },
19
20
  "dependencies": {
20
- "cross-env": "^10.1.0",
21
- "dotenv": "^18.0.4",
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
- "jet-paths": "^4.1.3",
27
- "jet-validators": "^2.3.1",
28
- "jsonfile": "^6.2.1",
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.2",
36
+ "@types/node": "^26.6.5",
40
37
  "@types/supertest": "^7.2.1",
41
38
  "connect-livereload": "^0.6.1",
42
- "eslint": "^10.11.0",
39
+ "eslint": "^10.12.0",
43
40
  "eslint-config-prettier": "^10.1.8",
44
- "eslint-plugin-n": "^18.3.0",
41
+ "eslint-plugin-n": "^18.4.1",
45
42
  "jiti": "^2.7.0",
46
43
  "livereload": "^0.10.3",
47
- "prettier": "^3.9.9",
48
- "shx": "^0.4.0",
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.70.1",
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 type { ParseError } from 'jet-validators/utils';
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
- }
@@ -1,4 +1,4 @@
1
- import type { ValueOf } from '../utils/validators';
1
+ import type { ValueOf } from '../utils/validators.ts';
2
2
 
3
3
  // ========================================================================= //
4
4
  // CONSTANTS //
@@ -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 = ValueOf<typeof NodeEnvs>;
15
+ export type NodeEnvs = z.infer<typeof NodeEnvsEnum>;
17
16
 
18
- export const EnvVars = {
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
- // ---- Validation
28
- // Validate the "environment variables"
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
- const isNodeEnv = isValueOf(NodeEnvs);
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 `tsx watch` restarts the server for a TypeScript change.
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';