create-epic-graphql-server 0.5.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/.eslintignore ADDED
@@ -0,0 +1,7 @@
1
+ .eslintrc.js
2
+ node_modules
3
+ .vscode
4
+ specs
5
+ bin
6
+ build
7
+ tsconfig.json
package/.eslintrc.js ADDED
@@ -0,0 +1,198 @@
1
+ module.exports = {
2
+ root: true,
3
+ parser: '@typescript-eslint/parser',
4
+ parserOptions: {
5
+ ecmaVersion: 2020,
6
+ sourceType: 'script',
7
+ project: ['./tsconfig.json'],
8
+ tsconfigRootDir: __dirname,
9
+ },
10
+ env: {
11
+ node: true,
12
+ es2021: true,
13
+ },
14
+ extends: [
15
+ 'plugin:@typescript-eslint/recommended',
16
+ ],
17
+ plugins: [
18
+ '@typescript-eslint',
19
+ ],
20
+ rules: {
21
+ // 📜 @typescript-eslint
22
+ 'quotes': 'off',
23
+ '@typescript-eslint/quotes': ['error', 'single'],
24
+ 'no-dupe-class-members': 'off', // handled by .ts
25
+ 'no-undef': 'off', // handled by .ts
26
+ '@typescript-eslint/consistent-type-assertions': 'error',
27
+ 'no-array-constructor': 'off',
28
+ '@typescript-eslint/no-array-constructor': 'error',
29
+ 'no-var': 'error', // makes 'no-redeclare' irrelevant
30
+ // 'no-redeclare': 'off', // see above
31
+ // '@typescript-eslint/no-redeclare': 'error', // see above
32
+ 'no-use-before-define': 'off',
33
+ '@typescript-eslint/no-use-before-define': [
34
+ 'error',
35
+ {
36
+ 'functions': true,
37
+ 'classes': true,
38
+ 'variables': true,
39
+ 'allowNamedExports': true,
40
+ 'enums': true,
41
+ 'typedefs': true,
42
+ 'ignoreTypeReferences': true,
43
+ },
44
+ ],
45
+ 'no-unused-expressions': 'off',
46
+ '@typescript-eslint/no-unused-expressions': [
47
+ 'error',
48
+ {
49
+ 'allowShortCircuit': true,
50
+ 'allowTernary': true,
51
+ 'allowTaggedTemplates': true,
52
+ },
53
+ ],
54
+ 'no-unused-vars': 'off',
55
+ '@typescript-eslint/no-unused-vars': [
56
+ 'error',
57
+ {
58
+ 'args': 'none',
59
+ 'ignoreRestSiblings': true,
60
+ },
61
+ ],
62
+ 'no-useless-constructor': 'off',
63
+ '@typescript-eslint/no-useless-constructor': 'error',
64
+ '@typescript-eslint/no-var-requires': 'off',
65
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
66
+ '@typescript-eslint/consistent-type-imports': [
67
+ 'warn',
68
+ {
69
+ prefer: 'type-imports',
70
+ disallowTypeAnnotations: false,
71
+ },
72
+ ],
73
+
74
+ // 📜 eslint
75
+ 'array-callback-return': 'error',
76
+ 'default-case': 'error',
77
+ 'dot-location': ['error', 'property'],
78
+ 'eqeqeq': ['error', 'always'],
79
+ 'new-parens': 'error',
80
+ 'no-caller': 'error',
81
+ 'no-cond-assign': ['error', 'except-parens'],
82
+ 'no-const-assign': 'error',
83
+ 'no-control-regex': 'error',
84
+ 'no-delete-var': 'error',
85
+ 'no-dupe-args': 'error',
86
+ 'no-dupe-keys': 'error',
87
+ 'no-duplicate-case': 'error',
88
+ 'no-empty-character-class': 'error',
89
+ 'no-empty-pattern': 'error',
90
+ 'no-eval': 'error',
91
+ 'no-ex-assign': 'error',
92
+ 'no-extend-native': 'error',
93
+ 'no-extra-bind': 'error',
94
+ 'no-extra-label': 'error',
95
+ 'no-fallthrough': 'error',
96
+ 'no-func-assign': 'error',
97
+ 'no-implied-eval': 'error',
98
+ 'no-invalid-regexp': 'error',
99
+ 'no-iterator': 'error',
100
+ 'no-label-var': 'error',
101
+ 'no-labels': [
102
+ 'error',
103
+ {
104
+ 'allowLoop': true,
105
+ 'allowSwitch': false,
106
+ },
107
+ ],
108
+ 'no-lone-blocks': 'error',
109
+ 'no-loop-func': 'error',
110
+ 'no-mixed-operators': [
111
+ 'error',
112
+ {
113
+ 'groups': [
114
+ ['&', '|', '^', '~', '<<', '>>', '>>>'],
115
+ ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
116
+ ['&&', '||'],
117
+ ['in', 'instanceof'],
118
+ ],
119
+ 'allowSamePrecedence': false,
120
+ },
121
+ ],
122
+ 'no-multi-str': 'error',
123
+ 'no-native-reassign': 'error',
124
+ 'no-negated-in-lhs': 'error',
125
+ 'no-new-func': 'error',
126
+ 'no-new-object': 'error',
127
+ 'no-new-symbol': 'error',
128
+ 'no-new-wrappers': 'error',
129
+ 'no-obj-calls': 'error',
130
+ 'no-octal': 'error',
131
+ 'no-octal-escape': 'error',
132
+ 'no-redeclare': 'error',
133
+ 'no-regex-spaces': 'error',
134
+ 'no-restricted-syntax': ['error', 'WithStatement'],
135
+ 'no-script-url': 'error',
136
+ 'no-self-assign': 'error',
137
+ 'no-self-compare': 'error',
138
+ 'no-sequences': 'error',
139
+ 'no-shadow-restricted-names': 'error',
140
+ 'no-sparse-arrays': 'error',
141
+ 'no-template-curly-in-string': 'error',
142
+ 'no-this-before-super': 'error',
143
+ 'no-throw-literal': 'error',
144
+ 'no-unreachable': 'error',
145
+ 'no-unused-labels': 'error',
146
+ 'no-useless-computed-key': 'error',
147
+ 'no-useless-concat': 'error',
148
+ 'no-useless-escape': 'error',
149
+ 'no-useless-rename': [
150
+ 'error',
151
+ {
152
+ 'ignoreDestructuring': false,
153
+ 'ignoreImport': false,
154
+ 'ignoreExport': false,
155
+ },
156
+ ],
157
+ 'no-with': 'error',
158
+ 'no-whitespace-before-property': 'error',
159
+ 'require-yield': 'error',
160
+ 'rest-spread-spacing': ['error', 'never'],
161
+ 'strict': ['error', 'never'],
162
+ 'unicode-bom': ['error', 'never'],
163
+ 'use-isnan': 'error',
164
+ 'valid-typeof': 'error',
165
+ 'getter-return': 'error',
166
+
167
+ // 📜 eslint, some additional styles
168
+ 'array-element-newline': ['error', 'consistent'],
169
+ 'comma-dangle': ['error', {
170
+ 'arrays': 'always-multiline',
171
+ 'objects': 'always-multiline',
172
+ 'imports': 'always-multiline',
173
+ 'exports': 'always-multiline',
174
+ // 'functions': 'never'
175
+ }],
176
+ 'semi': ['error', 'never'],
177
+ 'curly': ['error', 'multi-line'],
178
+ 'indent': [
179
+ 'error',
180
+ 2,
181
+ {
182
+ 'SwitchCase': 1,
183
+ },
184
+ ],
185
+ 'arrow-spacing': ['error'],
186
+ 'object-curly-spacing': ['error', 'always'],
187
+ 'array-bracket-spacing': ['error', 'never'],
188
+ 'no-irregular-whitespace': ['error'],
189
+ 'eol-last': ['error', 'always'],
190
+ 'no-trailing-spaces': [
191
+ 'error',
192
+ {
193
+ 'skipBlankLines': false,
194
+ 'ignoreComments': true,
195
+ },
196
+ ],
197
+ },
198
+ }
@@ -0,0 +1,2 @@
1
+ npm test
2
+ npm run 'lint'
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 daveKontro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FdaveKontro%2Fcreate-epic-graphql-server%2Fmain%2Fpackage.json&query=%24.version&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABnUlEQVR4nO2bT0vEMBDFE+lBjCgoHvMx9Puf/Rq5eRGUIEIhngpS2HRet+nLn/ldM8m+fTuZLs3EGpzXFMP7jnmnYJ23UDz6ASmGhM45E9SAG3D9OzD+dNAfCDIgxRAxOfWDZkB3IAY8F1NxPE/SQHHBqL34rZEWQ90CwriXoioKIM1YkQEpho/r5NSLbgG2ADablbK16r9m62kwfAZMbAHzPBtjjJkmuZRlzgIydw3dgD3ir/nCa7rfAls1rHsDtlADcoOtPwIlaAawBbBRAzJj96epKEyull00IMXwXUZOXegWYAtgowawBbBRA9gC2KgBbAFs1AC2ADZqAFsAGzWALYCNGsAWwCZ7cNjLW+HcAenwGaAGsAWwUQPYAthkDbDOP5wlpBTW+dvc+FYG9HA28JsbpHSIrFtcFtDOj//r7O0aoRhwVIvLEesMXwRFHdWt/iWWdIwPnwFqgCTIOi++gdEa0gz4LKqiAHpjRMjwBkC3LFt5HCK3R4fPAMgA6/xjKSFHYZ2HrveiGfAFxjP4QYLhLWCdf0Pn1Mwf/j9N4kR1HlsAAAAASUVORK5CYII=&label=version&labelColor=%23ff883e&color=%23fff5e3)
2
+ ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FdaveKontro%2Fcreate-epic-graphql-server%2Fmain%2Fpackage.json&query=%24.engines.node&logo=nodedotjs&label=node&labelColor=%23ff883e&color=%23fff5e3)
3
+ ![Static Badge](https://img.shields.io/badge/npm->=v10-%23fff5e3?logo=npm&labelColor=%23ff883e)
4
+
5
+ # Create Epic GraphQL Server
6
+ This module provides a configured [GraphQL](https://www.npmjs.com/package/graphql) server.
7
+
8
+ Having a preconfigured server will jump-start your project so you never have to begin from scratch.
9
+
10
+ Use GraphQL to unify you data into a single API. Take advantage of the powerful, intuitive GraphQL query language.
11
+
12
+ Interact with this project template live [here](https://www.createepicgraphqlserver.com/).
13
+
14
+ ## installation
15
+ first install globally
16
+ ```
17
+ npm install -g create-epic-graphql-server
18
+ ```
19
+
20
+ then create your project
21
+ ```
22
+ npx create-epic-graphql-server --name={my-project}
23
+ ```
24
+
25
+ ## usage
26
+ spin up the mock database (described below) `npm run dev:db`
27
+
28
+ now spin up the development server `npm run dev`
29
+
30
+ congrats, your API is now live!
31
+
32
+ ### Graph*i*QL
33
+ use the Graph*i*QL IDE to interact with your development server's API
34
+
35
+ this project provides the [ruru](https://www.npmjs.com/package/ruru) Graph*i*QL interface on `http://localhost:3000` (by default) during development
36
+
37
+ sample queries are provided later in this document
38
+
39
+ ### mock database
40
+ in order to get up and running quickly without the need to initially plug into a real database, this project provides a mock database via [json-server](https://www.npmjs.com/package/json-server)
41
+
42
+ the database starts when you run `npm run dev:db`, which automatically creates and runs of the file `db.json5`; the db file is seeded upon each startup from `db-seed.json`
43
+
44
+ see file `/src/config/db.ts` to understand how the database works; you will likely, eventually remove this file when you plug into a real database and replace the contents of `/src/models` with actual database models... that said, the provided pattern gives you scaffolding to build around
45
+
46
+ ⚠️ please note this mock database is not intended or suited for production use ⚠️
47
+
48
+ ## schema and types
49
+ the schema sits at the heart of the GraphQL service
50
+
51
+ the types describe what data you can query, and the schema is the collection of what the service provides
52
+
53
+ a sample schema has been configured in `/src/schema/schema` with model validation, read all, read on, mutations, etc... again, your usecase will necessitate alterations to the schema, but the provided pattern might help you along the way
54
+
55
+ ## test suite
56
+ the test suite is enabled to run unit and integration tests
57
+
58
+ to create a test add a file tp `specs/` and follow this file naming format: `*.spec.ts`
59
+
60
+ tests run automatically during development via `npm run dev` or the test suite stand alone like so
61
+
62
+ ```
63
+ npm run test
64
+ npm run test:watch
65
+ ```
66
+
67
+ ## linting
68
+ linting rules are in `.eslintrc.js`; install the [ESLint](https://www.npmjs.com/package/eslint) pluggin if using vscode
69
+
70
+ ```
71
+ npm run lint
72
+ ```
73
+
74
+ ## custom logger
75
+ A customized [winston](https://www.npmjs.com/package/winston) logger instance resides in `utilities/logger`, with usage found thoughout the codebase methods are provide to log a timestamp or the current node environment
76
+
77
+ ## pre-commit
78
+ scripts in `.husky/pre-commit` are run on commits for quality control
79
+
80
+ add or remove scripts you'd like run before code is commited
81
+
82
+ ## environmental settings
83
+ you can create a `.env` file at the project root and add the following variables
84
+
85
+ also add any additional variables your project needs
86
+
87
+ ### develop (dev server)
88
+ ```
89
+ # optional but recommended
90
+ NODE_ENV=development
91
+ PORT={port number}
92
+ # optional
93
+ JSON_SERVER_PORT={dev db port, default 3210}
94
+ ```
95
+
96
+ ### production (build)
97
+ ```
98
+ # optional but recommended
99
+ NODE_ENV=production
100
+ PORT={port number}
101
+ ```
102
+
103
+ ## query samples
104
+ Run these queries against the development server to get a feel the GraphQL query language.
105
+
106
+ ### get all orders
107
+ ```
108
+ {
109
+ orders {
110
+ __typename
111
+ id
112
+ name
113
+ notes
114
+ status
115
+ customer {
116
+ __typename
117
+ name
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### get all customers
124
+ ```
125
+ {
126
+ customers {
127
+ id
128
+ name
129
+ email
130
+ phone
131
+ }
132
+ }
133
+ ```
134
+
135
+ ### get an order by id
136
+ ```
137
+ {
138
+ order(id: "2") {
139
+ id
140
+ name
141
+ status
142
+ customer {
143
+ name
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### get orders by status
150
+ ```
151
+ {
152
+ orders(filter: { status: "Not Started" }) {
153
+ id
154
+ name
155
+ status
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### create a customer
161
+ ```
162
+ mutation {
163
+ addCustomer(
164
+ name: "Tom Hill",
165
+ email: "tom@example.com",
166
+ phone: "555-5555"
167
+ ) {
168
+ id
169
+ name
170
+ email
171
+ phone
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### create an order
177
+ - use id from new customer "Tom Hill"
178
+
179
+ ```
180
+ mutation {
181
+ addOrder(
182
+ name: productOne,
183
+ status: notStarted,
184
+ customerId: "{id}"
185
+ ) {
186
+ id
187
+ name
188
+ status
189
+ customer {
190
+ id
191
+ name
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### update and order
198
+ - use id from new order created above
199
+
200
+ ```
201
+ mutation {
202
+ updateOrder(
203
+ id: "{id}",
204
+ status: processing,
205
+ ) {
206
+ id
207
+ name
208
+ status
209
+ customer {
210
+ id
211
+ name
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### delete a customer by id
218
+ - use id from new customer "Tom Hill"
219
+ - resolver is setup to delete associated orders too
220
+
221
+ ```
222
+ mutation {
223
+ deleteCustomer(id: "{id}") {
224
+ id
225
+ name
226
+ }
227
+ }
228
+ ```
@@ -0,0 +1,81 @@
1
+ #! /usr/bin/env node
2
+ const { writeFile } = require('fs/promises')
3
+ const { execSync } = require('child_process')
4
+ const path = require('path')
5
+ const yargs = require('yargs/yargs')
6
+ const { hideBin } = require('yargs/helpers')
7
+ const packageJson = require('../package.json')
8
+
9
+ const run = async () => {
10
+ const repo = 'https://github.com/daveKontro/create-epic-graphql-server/tarball/main'
11
+
12
+ const execCommand = (command) => {
13
+ try {
14
+ execSync(command, { stdio: 'inherit' })
15
+ } catch (error) {
16
+ console.error(`${command} failed`, error)
17
+ process.exit(1)
18
+ }
19
+
20
+ return true
21
+ }
22
+
23
+ // consume arguments
24
+ const argv = yargs(hideBin(process.argv)).argv
25
+
26
+ if (!argv.name) {
27
+ console.error('WARNING add a project name like so: npx create-epic-graphql-server --name={my-project}')
28
+ process.exit(1)
29
+ }
30
+
31
+ const projectPaths = {
32
+ root: path.resolve('.', argv.name),
33
+ get bin() { return path.resolve(this.root, 'bin') },
34
+ get env() { return path.resolve(this.root, '.env') },
35
+ get packageJson() { return path.resolve(this.root, 'package.json') },
36
+ }
37
+
38
+ const isNameDotCommand = (argv.name === '.')
39
+ const projectName = isNameDotCommand ? path.basename(process.cwd()) : argv.name
40
+
41
+ console.info('')
42
+ console.info(`Your project name is: ${projectName}`)
43
+ console.info('')
44
+
45
+ // create project
46
+ execCommand(`curl -L ${repo} | tar zx --one-top-level=${argv.name} --strip-components 1`)
47
+
48
+ // groom project
49
+ if (packageJson.hasOwnProperty('name')) packageJson.name = projectName
50
+ if (packageJson.hasOwnProperty('version')) packageJson.version = '1.0.0'
51
+ if (packageJson.hasOwnProperty('description')) packageJson.description = ''
52
+ if (packageJson.hasOwnProperty('author')) packageJson.author = ''
53
+ if (packageJson.hasOwnProperty('bin')) delete packageJson.bin
54
+ if (packageJson.hasOwnProperty('repository')) delete packageJson.repository
55
+ if (packageJson.dependencies.hasOwnProperty('yargs')) delete packageJson.dependencies.yargs
56
+
57
+ execCommand(`rm ${projectPaths.packageJson}`)
58
+ execCommand(`rm -rf ${projectPaths.bin}`)
59
+
60
+ try {
61
+ await writeFile(projectPaths.packageJson, JSON.stringify(packageJson, null, 2), {
62
+ encoding: 'utf8',
63
+ })
64
+ } catch (error) {
65
+ console.error('package.json creation failed', error)
66
+ process.exit(1)
67
+ }
68
+
69
+ execCommand(`touch ${projectPaths.env}`)
70
+
71
+ // install project dependencies
72
+ execCommand(`npm --prefix ${projectPaths.root} install`)
73
+
74
+ // finish up
75
+ console.info('')
76
+ console.info('Thanks for using Create Epic GraphQL Server!')
77
+
78
+ process.exit(0)
79
+ }
80
+
81
+ run()
package/db-seed.json5 ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ customers: [
3
+ {
4
+ id: '1',
5
+ name: 'Jane Doe',
6
+ email: 'jane@example.com',
7
+ phone: '555-1234'
8
+ },
9
+ {
10
+ id: '2',
11
+ name: 'John Smith',
12
+ email: 'john@example.com',
13
+ phone: '555-9876'
14
+ }
15
+ ],
16
+ orders: [
17
+ {
18
+ id: '1',
19
+ name: 'Product One',
20
+ notes: 'Urgent delivery',
21
+ status: 'Processing',
22
+ customerId: '1'
23
+ },
24
+ {
25
+ id: '2',
26
+ name: 'Product Two',
27
+ status: 'Not Started',
28
+ customerId: '2'
29
+ },
30
+ {
31
+ id: '3',
32
+ name: 'Product Three',
33
+ status: 'Not Started',
34
+ customerId: '1'
35
+ }
36
+ ]
37
+ }
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "create-epic-graphql-server",
3
+ "version": "0.5.0",
4
+ "description": "a configured graphql server",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "create-epic-graphql-server": "bin/create-epic-graphql-server.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc --build",
11
+ "start": "node ./build/index.js",
12
+ "dev": "concurrently \"tsx watch ./src/index.ts\" \"npm run test:watch:min\"",
13
+ "dev:db": "dotenv -e .env -- bash -c 'cp db-seed.json5 db.json5 && json-server db.json5 --port ${JSON_SERVER_PORT:-3210}'",
14
+ "test": "mocha --require ts-node/register specs/**/*.ts",
15
+ "test:watch": "mocha --require ts-node/register specs/**/*.ts --watch --watch-files src/**/*.ts,specs/**/*.ts",
16
+ "test:watch:min": "mocha --require ts-node/register specs/**/*.ts --watch --watch-files src/**/*.ts,specs/**/*.ts --reporter min",
17
+ "lint": "eslint src/**/*.{ts,json}",
18
+ "lint:fix": "eslint --fix src/**/*.{ts,json}",
19
+ "prepare": "husky"
20
+ },
21
+ "keywords": [
22
+ "template",
23
+ "server",
24
+ "graphql",
25
+ "node",
26
+ "express",
27
+ "typescript",
28
+ "eslint",
29
+ "mocha",
30
+ "chai",
31
+ "sinon",
32
+ "supertest",
33
+ "javascript"
34
+ ],
35
+ "author": "David Kontrovitz",
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "chalk": "4.1.2",
39
+ "concurrently": "9.2.0",
40
+ "cors": "2.8.5",
41
+ "dotenv-cli": "8.0.0",
42
+ "dotenv-flow": "4.1.0",
43
+ "express": "5.1.0",
44
+ "graphql": "16.11.0",
45
+ "graphql-http": "1.22.4",
46
+ "helmet": "8.1.0",
47
+ "husky": "9.1.7",
48
+ "json-server": "1.0.0-beta.3",
49
+ "ruru": "2.0.0-beta.22",
50
+ "winston": "3.17.0",
51
+ "winston-daily-rotate-file": "5.0.0",
52
+ "yargs": "17.7.2",
53
+ "zod": "3.24.4"
54
+ },
55
+ "devDependencies": {
56
+ "@types/chai": "4.3.11",
57
+ "@types/chalk": "0.4.31",
58
+ "@types/dotenv-flow": "3.3.3",
59
+ "@types/express": "5.0.2",
60
+ "@types/mocha": "10.0.10",
61
+ "@types/node": "22.15.18",
62
+ "@types/sinon": "17.0.4",
63
+ "@types/supertest": "6.0.3",
64
+ "@typescript-eslint/eslint-plugin": "6.5.0",
65
+ "@typescript-eslint/parser": "6.5.0",
66
+ "chai": "4.3.10",
67
+ "eslint": "8.57.1",
68
+ "mocha": "9.2.2",
69
+ "sinon": "21.0.0",
70
+ "supertest": "7.1.1",
71
+ "ts-node": "10.9.2",
72
+ "tsx": "4.19.4",
73
+ "typescript": "5.8.3"
74
+ }
75
+ }
@@ -0,0 +1,41 @@
1
+ import { expect } from 'chai'
2
+ import { customerSchema } from '../src/models/Customer'
3
+
4
+ describe('customerSchema', () => {
5
+ it('accepts a valid customer object', () => {
6
+ const data = {
7
+ name: 'John Doe',
8
+ email: 'john@example.com',
9
+ phone: '555-1234'
10
+ }
11
+
12
+ const parsed = customerSchema.parse(data)
13
+ expect(parsed).to.deep.equal(data)
14
+ })
15
+
16
+ it('allows an optional id', () => {
17
+ const data = {
18
+ id: 'cust-1',
19
+ name: 'Jane Doe',
20
+ email: 'jane@example.com',
21
+ phone: '555-5678'
22
+ }
23
+
24
+ const parsed = customerSchema.parse(data)
25
+ expect(parsed).to.deep.equal(data)
26
+ })
27
+
28
+ it('fails if email is invalid', () => {
29
+ const data = {
30
+ name: 'Invalid Email',
31
+ email: 'not-an-email',
32
+ phone: '555-0000'
33
+ }
34
+
35
+ expect(() => customerSchema.parse(data)).to.throw()
36
+ })
37
+
38
+ it('fails if required fields are missing', () => {
39
+ expect(() => customerSchema.parse({})).to.throw()
40
+ })
41
+ })