gazan-init 0.1.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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +722 -0
  3. package/bin/gazan.js +17 -0
  4. package/package.json +54 -0
  5. package/src/cli/commands/init.js +126 -0
  6. package/src/cli/index.js +22 -0
  7. package/src/config/aliases.js +130 -0
  8. package/src/config/normalize.js +65 -0
  9. package/src/generators/aliasResolver.js +68 -0
  10. package/src/generators/aliasRuntime.js +79 -0
  11. package/src/generators/database/mongoNative.js +45 -0
  12. package/src/generators/database/mongoose.js +32 -0
  13. package/src/generators/database/prisma.js +61 -0
  14. package/src/generators/engine.js +88 -0
  15. package/src/generators/entity/sensitiveFields.js +12 -0
  16. package/src/generators/entity/toApp.js +325 -0
  17. package/src/generators/entity/toMongoose.js +180 -0
  18. package/src/generators/entity/toPrisma.js +233 -0
  19. package/src/generators/entity/toZod.js +70 -0
  20. package/src/generators/env.js +123 -0
  21. package/src/generators/errors.js +67 -0
  22. package/src/generators/features/auth.js +163 -0
  23. package/src/generators/features/bullmq.js +96 -0
  24. package/src/generators/features/redis.js +34 -0
  25. package/src/generators/features/socket.js +45 -0
  26. package/src/generators/gitignore.js +19 -0
  27. package/src/generators/index.js +317 -0
  28. package/src/generators/middlewares.js +167 -0
  29. package/src/generators/packageJson.js +107 -0
  30. package/src/generators/paths.js +45 -0
  31. package/src/generators/project.js +189 -0
  32. package/src/generators/readme.js +177 -0
  33. package/src/generators/shutdown.js +113 -0
  34. package/src/generators/stripSensitiveFieldsHelper.js +50 -0
  35. package/src/generators/syntax.js +65 -0
  36. package/src/generators/tsconfig.js +32 -0
  37. package/src/parser/entity/errors.js +21 -0
  38. package/src/parser/entity/parse.js +417 -0
  39. package/src/parser/entity/schema.js +104 -0
  40. package/src/prompts/index.js +186 -0
  41. package/src/utils/fsSafety.js +17 -0
  42. package/src/utils/strings.js +110 -0
package/README.md ADDED
@@ -0,0 +1,722 @@
1
+ <p align="center"><strong>GAZAN</strong></p>
2
+ <p align="center">Interactive backend project generator for Node.js</p>
3
+
4
+ <p align="center">
5
+ <img alt="license" src="https://img.shields.io/badge/license-MIT-blue.svg">
6
+ <img alt="node" src="https://img.shields.io/badge/node-%3E%3D18-brightgreen">
7
+ </p>
8
+
9
+ GAZAN is a CLI that asks a short series of questions about the backend you want — module
10
+ system, language, architecture, database, auth, infrastructure — and generates a complete,
11
+ runnable Express project from the answers. Nothing you don't select gets generated: no unused
12
+ dependencies, no dead directories, no disabled-feature code paths.
13
+
14
+ ```bash
15
+ npx gazan-init init
16
+ ```
17
+
18
+ ## Why GAZAN?
19
+
20
+ Most "backend starter" templates are frozen snapshots: one language, one database, one
21
+ architecture, and you delete what you don't need. GAZAN generates the project *from* your
22
+ choices instead, so the result only ever contains what you asked for — and the generator itself
23
+ is tested against dozens of real configuration combinations, not just the default one.
24
+
25
+ It is not a repository-layer framework, an ORM, or a runtime library your generated project
26
+ depends on. Once generated, the project is a plain Express app with no reference back to GAZAN.
27
+
28
+ ## Features
29
+
30
+ - **CommonJS or ES Modules**, **JavaScript or TypeScript** — correct syntax, extensions, and
31
+ `tsconfig.json`/`package.json` wiring for whichever combination you pick.
32
+ - **MVC or HMVC** architecture, with or without a `src/` directory.
33
+ - **PostgreSQL + Prisma**, **MongoDB + Mongoose**, **MongoDB (native driver)**, or no database.
34
+ - **`entity.json`** — an optional schema file that, when provided, generates real Prisma models
35
+ or Mongoose schemas plus matching Zod validators, services, controllers, and routes.
36
+ - **Redis, BullMQ, Socket.IO** — each generated only when selected, sharing one Redis connection.
37
+ - **Authentication** — email/password (bcrypt), JWT, refresh tokens; OAuth is a documented stub
38
+ (see [Authentication](#authentication)).
39
+ - **Module aliases** (`@/services/x` instead of `../../services/x`) — optional, generated for
40
+ whichever directories your configuration actually produces, and wired to actually resolve at
41
+ runtime for every module system / language combination (see [Module Aliases](#module-aliases)).
42
+ - **Security baseline** — Helmet, CORS, request validation (Zod), rate limiting (in-memory or
43
+ Redis-backed), centralized error handling, environment validation at startup, graceful shutdown,
44
+ and automatic stripping of password/secret-like fields from API responses.
45
+ - **No repository layer.** Services talk to the database directly:
46
+ `Route → Controller → Service → Database`.
47
+
48
+ ## Supported stack
49
+
50
+ | Concern | Options |
51
+ |---|---|
52
+ | Module system | CommonJS, ES Modules |
53
+ | Language | JavaScript, TypeScript |
54
+ | Architecture | MVC, HMVC |
55
+ | Database | PostgreSQL + Prisma, MongoDB + Mongoose, MongoDB (native driver), none |
56
+ | Infrastructure | Redis, BullMQ, Socket.IO |
57
+ | Authentication | Email/password, JWT, refresh tokens, OAuth (stub) |
58
+ | Module aliases | Optional, on by default |
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ npx gazan-init init
64
+ ```
65
+
66
+ Or install the CLI globally:
67
+
68
+ ```bash
69
+ npm install -g gazan-init
70
+ gazan init
71
+ ```
72
+
73
+ From a local clone:
74
+
75
+ ```bash
76
+ git clone git@github.com:korveniq/Gazan.git
77
+ cd Gazan
78
+ npm install
79
+ node bin/gazan.js init
80
+ ```
81
+
82
+ Requires **Node.js 18 or later** — `gazan` checks this itself at startup and exits with a clear
83
+ message if your Node version is too old.
84
+
85
+ ## Quick start
86
+
87
+ ```bash
88
+ node bin/gazan.js init
89
+ ```
90
+
91
+ GAZAN asks its questions in order, then generates the project:
92
+
93
+ ```text
94
+ ┌ GAZAN — backend project initializer
95
+
96
+ ◇ Project name
97
+ │ my-api
98
+
99
+ ◇ Which module system do you want?
100
+ │ ES Modules (MJS)
101
+
102
+ ◇ Which language do you want?
103
+ │ TypeScript
104
+
105
+ ◇ Do you want to enable module/path aliases? (e.g. @/services/x instead of ../../services/x)
106
+ │ Yes
107
+
108
+ ◇ Which database do you want?
109
+ │ PostgreSQL + Prisma
110
+
111
+ ◇ Which architecture do you want?
112
+ │ MVC
113
+
114
+ ◇ Use src/ directory?
115
+ │ Yes
116
+
117
+ ◇ Do you need Socket.IO?
118
+ │ No
119
+
120
+ ◇ Do you need BullMQ workers?
121
+ │ No
122
+
123
+ ◇ Use Redis for rate limiting? (No = in-memory rate limiting)
124
+ │ No
125
+
126
+ ◇ Do you need authentication?
127
+ │ Yes
128
+
129
+ ◇ Which authentication methods?
130
+ │ JWT
131
+
132
+ ◇ Do you have an entity.json file?
133
+ │ No
134
+
135
+ └ Configuration collected.
136
+
137
+ ✔ Project structure created
138
+ ✔ Environment & error handling configured
139
+ ✔ Security middleware configured
140
+ ✔ Database configured
141
+ ✔ Redis configured
142
+ ✔ BullMQ configured
143
+ ✔ Socket.IO configured
144
+ ✔ Authentication configured
145
+ ✔ Entity models generated
146
+ ✔ Module aliases configured
147
+ ✔ README generated
148
+
149
+ Project initialized successfully.
150
+
151
+ Next steps:
152
+
153
+ cd my-api
154
+ npm install
155
+ npm run dev
156
+ ```
157
+
158
+ If the target directory already has content in it (beyond a stray `.git`), GAZAN asks before
159
+ touching anything — see [Existing directories](#existing-directories).
160
+
161
+ ## Generated project structure
162
+
163
+ **MVC**, with `src/`, PostgreSQL + Prisma, Redis, and aliases enabled:
164
+
165
+ ```text
166
+ my-api/
167
+ ├── src/
168
+ │ ├── controllers/
169
+ │ ├── routes/
170
+ │ │ └── index.js
171
+ │ ├── services/
172
+ │ ├── validators/
173
+ │ ├── middlewares/
174
+ │ │ ├── error-handler.js
175
+ │ │ ├── not-found.js
176
+ │ │ ├── rate-limit.js
177
+ │ │ └── validate.js
178
+ │ ├── helpers/
179
+ │ │ ├── env.js
180
+ │ │ └── errors.js
181
+ │ ├── utils/
182
+ │ │ ├── shutdown.js
183
+ │ │ └── validate-env.js
184
+ │ ├── configs/
185
+ │ │ ├── db/
186
+ │ │ └── redis/
187
+ │ ├── app.js
188
+ │ └── server.js
189
+ ├── prisma/
190
+ │ └── schema.prisma
191
+ ├── jsconfig.json
192
+ ├── .env
193
+ ├── .env.example
194
+ ├── .gitignore
195
+ ├── package.json
196
+ └── README.md
197
+ ```
198
+
199
+ `workers/` only exists when BullMQ is enabled, `socket/` only when Socket.IO is enabled,
200
+ `prisma/` only for PostgreSQL + Prisma, `models/` only for MongoDB + Mongoose, `tsconfig.json`
201
+ only for TypeScript, and `jsconfig.json` / `alias-loader.mjs` only when aliases are enabled (see
202
+ [Module Aliases](#module-aliases) for which files appear for which module system).
203
+
204
+ **HMVC** groups `controllers/routes/services/validators` per entity instead of by layer:
205
+
206
+ ```text
207
+ src/
208
+ ├── modules/
209
+ │ ├── user/
210
+ │ │ ├── controllers/
211
+ │ │ ├── routes/
212
+ │ │ ├── services/
213
+ │ │ └── validators/
214
+ │ └── post/
215
+ │ ├── controllers/
216
+ │ ├── routes/
217
+ │ ├── services/
218
+ │ └── validators/
219
+ ├── routes/
220
+ │ └── index.js # aggregates every module's router
221
+ ├── middlewares/
222
+ ├── helpers/
223
+ ├── utils/
224
+ └── configs/
225
+ ```
226
+
227
+ ## Architecture
228
+
229
+ Both architectures follow the same request flow — **there is no repository layer**:
230
+
231
+ ```text
232
+ MVC: Route → Controller → Service → Database
233
+ HMVC: Module Route → Module Controller → Module Service → Database
234
+ ```
235
+
236
+ Controllers and services are classes. A controller is constructed with a service instance
237
+ (dependency injection) and contains no business logic; a service is the only thing that talks to
238
+ the database. Routes stay thin — they wire a validator and a controller method together and
239
+ nothing else.
240
+
241
+ ## `entity.json`
242
+
243
+ An optional JSON file you can point GAZAN at during `init`. When provided, it becomes the source
244
+ of truth for generated database models, Zod validators, services, controllers, and routes — one
245
+ CRUD endpoint set per model (`POST /`, `GET /`, `GET /:id`, `PATCH /:id`, `DELETE /:id`), mounted
246
+ under `/api/<pluralized-model-name>`.
247
+
248
+ ```text
249
+ entity.json → Entity Parser (schema + semantic validation) → Normalized Entity Model → Database Generator
250
+ ```
251
+
252
+ Invalid files fail with precise, field-level errors — not a bare "invalid JSON":
253
+
254
+ ```text
255
+ models[1].relations.author.model:
256
+ unknown model 'Userr'. Did you mean 'User'?
257
+ ```
258
+
259
+ ### Basic example
260
+
261
+ ```json
262
+ {
263
+ "models": [
264
+ {
265
+ "name": "User",
266
+ "fields": {
267
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
268
+ "name": { "type": "string", "required": true },
269
+ "email": { "type": "string", "required": true, "unique": true },
270
+ "age": { "type": "integer" }
271
+ }
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ ### Realistic example
278
+
279
+ A blog-shaped schema exercising UUID primary keys, an enum, unique + indexed fields, a nullable
280
+ optional relation, a one-to-many, and a many-to-many:
281
+
282
+ ```json
283
+ {
284
+ "models": [
285
+ {
286
+ "name": "Role",
287
+ "fields": {
288
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
289
+ "name": { "type": "string", "required": true, "unique": true }
290
+ }
291
+ },
292
+ {
293
+ "name": "User",
294
+ "fields": {
295
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
296
+ "name": { "type": "string", "required": true, "length": 120 },
297
+ "email": { "type": "string", "required": true, "unique": true, "index": true },
298
+ "passwordHash": { "type": "string", "required": true },
299
+ "roleId": { "type": "uuid", "required": true }
300
+ },
301
+ "relations": {
302
+ "role": { "type": "belongsTo", "model": "Role", "foreignKey": "roleId" }
303
+ }
304
+ },
305
+ {
306
+ "name": "Category",
307
+ "fields": {
308
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
309
+ "name": { "type": "string", "required": true, "unique": true }
310
+ }
311
+ },
312
+ {
313
+ "name": "Tag",
314
+ "fields": {
315
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
316
+ "name": { "type": "string", "required": true, "unique": true }
317
+ },
318
+ "relations": {
319
+ "posts": { "type": "belongsToMany", "model": "Post", "through": "PostTag" }
320
+ }
321
+ },
322
+ {
323
+ "name": "Post",
324
+ "fields": {
325
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
326
+ "title": { "type": "string", "required": true, "length": 200, "index": true },
327
+ "slug": { "type": "string", "required": true, "unique": true },
328
+ "body": { "type": "text", "required": false, "nullable": true },
329
+ "status": { "type": "enum", "values": ["DRAFT", "PUBLISHED", "ARCHIVED"], "default": "DRAFT", "required": true },
330
+ "published": { "type": "boolean", "default": false, "required": true },
331
+ "authorId": { "type": "uuid", "required": true },
332
+ "categoryId": { "type": "uuid", "required": false, "nullable": true }
333
+ },
334
+ "relations": {
335
+ "author": { "type": "belongsTo", "model": "User", "foreignKey": "authorId" },
336
+ "category": { "type": "belongsTo", "model": "Category", "foreignKey": "categoryId" },
337
+ "comments": { "type": "hasMany", "model": "Comment", "foreignKey": "postId" },
338
+ "tags": { "type": "belongsToMany", "model": "Tag", "through": "PostTag" }
339
+ }
340
+ },
341
+ {
342
+ "name": "Comment",
343
+ "fields": {
344
+ "id": { "type": "uuid", "primaryKey": true, "default": "uuid" },
345
+ "body": { "type": "text", "required": true },
346
+ "postId": { "type": "uuid", "required": true },
347
+ "authorId": { "type": "uuid", "required": true }
348
+ },
349
+ "relations": {
350
+ "post": { "type": "belongsTo", "model": "Post", "foreignKey": "postId" },
351
+ "author": { "type": "belongsTo", "model": "User", "foreignKey": "authorId" }
352
+ }
353
+ }
354
+ ]
355
+ }
356
+ ```
357
+
358
+ Both examples above are copy-pasteable and are exercised directly by GAZAN's own test suite
359
+ against both Prisma and Mongoose.
360
+
361
+ ### Field types
362
+
363
+ | Type | Description | PostgreSQL (Prisma) | MongoDB (Mongoose) |
364
+ |---|---|---|---|
365
+ | `string` | Short text | `String` | `String` |
366
+ | `text` | Long text | `String` | `String` |
367
+ | `number` | Generic number | `Float` | `Number` |
368
+ | `integer` | Whole number | `Int` | `Number` |
369
+ | `float` | Floating point | `Float` | `Number` |
370
+ | `boolean` | True/false | `Boolean` | `Boolean` |
371
+ | `date` | Calendar date | `DateTime` | `Date` |
372
+ | `datetime` | Date + time | `DateTime` | `Date` |
373
+ | `uuid` | UUID string | `String` (`@default(uuid())` when `default: "uuid"`) | `String` |
374
+ | `json` | Arbitrary JSON | `Json` | `Mixed` |
375
+ | `enum` | Fixed set of values (needs `values`) | Generated Prisma `enum` | `String` with a schema-level enum validator |
376
+ | `decimal` | Fixed-precision number | `Decimal` | `Decimal128` |
377
+ | `bigint` | Large integer | `BigInt` | `Mixed` (Mongoose has no dedicated bigint type) |
378
+
379
+ **MongoDB primary keys must be `uuid` or omitted.** Every model's primary key is routed through
380
+ Mongo's native `_id` rather than a redundant parallel field; GAZAN generates a UUID string default
381
+ for it. A primary key of any other type (or `autoIncrement`, which Mongo has no native equivalent
382
+ for) is rejected at validation time rather than silently approximated.
383
+
384
+ ### Field attributes
385
+
386
+ | Attribute | Meaning |
387
+ |---|---|
388
+ | `required` | Field must be present; cannot be combined with `nullable: true`. |
389
+ | `nullable` | Column/field may be `null`. Not allowed on a `primaryKey` field. |
390
+ | `unique` | Unique constraint (`@unique` in Prisma, `unique: true` in Mongoose). |
391
+ | `index` | Single-column index (`@@index` in Prisma, `index: true` in Mongoose). |
392
+ | `default` | Default value. `"uuid"` is a sentinel meaning "generate one"; `"now"` on a `date`/`datetime` field means `now()`. |
393
+ | `primaryKey` | Marks the primary key. At most one per model. |
394
+ | `autoIncrement` | Postgres/Prisma only (`integer`/`bigint`); rejected for MongoDB. |
395
+ | `length` | Max string length (`@db.VarChar(n)` in Prisma, `maxlength` in Mongoose). |
396
+ | `min` / `max` | Numeric bounds, enforced in the generated Zod validator (and Mongoose `min`/`max`). |
397
+ | `values` | Required for `type: "enum"` — the list of allowed values. |
398
+
399
+ ### Relations
400
+
401
+ Four relation types, declared under a model's `relations` object:
402
+
403
+ ```json
404
+ {
405
+ "models": [
406
+ {
407
+ "name": "Post",
408
+ "fields": {
409
+ "authorId": { "type": "uuid", "required": true }
410
+ },
411
+ "relations": {
412
+ "author": { "type": "belongsTo", "model": "User", "foreignKey": "authorId" }
413
+ }
414
+ }
415
+ ]
416
+ }
417
+ ```
418
+
419
+ - **`belongsTo`** — this model holds the foreign key (`foreignKey` required).
420
+ - **`hasMany`** / **`hasOne`** — the *other* model holds the foreign key (`foreignKey` required).
421
+ You only need to declare this when you want to name or customize the reverse side yourself —
422
+ GAZAN auto-derives a reverse array field for any `belongsTo` that doesn't have one, including
423
+ disambiguating multiple relations to the same model and self-relations.
424
+ - **`belongsToMany`** — many-to-many (`through` required, naming the join). **Both models must
425
+ declare it, with a matching `through` value** — a one-sided declaration is rejected at
426
+ validation time rather than silently generating a broken schema.
427
+
428
+ **Many-to-many is schema-only.** `belongsToMany` is correctly represented in the generated Prisma
429
+ schema (implicit join table) and Mongoose model (array of refs on both sides), but the generated
430
+ CRUD `create`/`update` endpoints do not read or write it — Prisma's nested-write shape
431
+ (`connect: [...]`) and Mongoose's plain array differ enough that GAZAN doesn't attempt a one-size
432
+ implementation. Manage join-table writes through your own service code.
433
+
434
+ ## Database support
435
+
436
+ | Mode | Notes |
437
+ |---|---|
438
+ | PostgreSQL + Prisma | `prisma/schema.prisma` generated from `entity.json` (or a starter model if none is given, so `prisma generate` works immediately). |
439
+ | MongoDB + Mongoose | Models under `src/models/`, `_id`-based primary keys (see above). |
440
+ | MongoDB (native driver) | No Mongoose — a thin `MongoClient` wrapper with a duplicate-connect guard. |
441
+ | None | No database code, no DB dependency, no `DATABASE_URL`/`MONGODB_URI` requirement. |
442
+
443
+ ## Authentication
444
+
445
+ Selected independently — only what you pick is generated:
446
+
447
+ | Method | What's generated |
448
+ |---|---|
449
+ | Email/password | `helpers/bcrypt.js` (`hashPassword`/`comparePassword`) |
450
+ | JWT | `helpers/jwt.js` (`signAccessToken`/`verifyAccessToken`) + `middlewares/auth.js` |
451
+ | Refresh tokens | Adds `signRefreshToken`/`verifyRefreshToken` to `helpers/jwt.js` |
452
+ | OAuth | **Stub only** — see below |
453
+
454
+ > **OAuth is currently a documented integration stub.** GAZAN scaffolds
455
+ > `OAUTH_CLIENT_ID`/`OAUTH_CLIENT_SECRET`/`OAUTH_CALLBACK_URL` env vars and a
456
+ > `helpers/oauth.stub.*` file that documents what to build and throws if called — it does not
457
+ > provide a complete, provider-specific OAuth flow. No OAuth client library is installed. GAZAN
458
+ > prints an explicit warning about this after generation.
459
+
460
+ Password/secret/hash-like fields declared in `entity.json` (matching `/password|secret|hash/i`)
461
+ are automatically stripped from every generated CRUD response, regardless of database backend.
462
+
463
+ ## Module aliases
464
+
465
+ Optional (on by default) — generated from whichever directories your specific configuration
466
+ actually produces, never for a feature you didn't enable.
467
+
468
+ **MVC**, PostgreSQL + Redis + Socket.IO + BullMQ:
469
+
470
+ ```text
471
+ @ → src
472
+ @controllers → src/controllers
473
+ @services → src/services
474
+ @routes → src/routes
475
+ @validators → src/validators
476
+ @middlewares → src/middlewares
477
+ @utils → src/utils
478
+ @helpers → src/helpers
479
+ @configs → src/configs
480
+ @db → src/configs/db
481
+ @redis → src/configs/redis
482
+ @socket → src/socket
483
+ @workers → src/workers
484
+ ```
485
+
486
+ ```ts
487
+ import { prisma } from '@db/index';
488
+ import { UserService } from '@services/user.service';
489
+ ```
490
+
491
+ **HMVC** additionally gets one alias per entity model (pluralized, pointing at that model's real
492
+ module directory — e.g. a `User` model's module lives at `src/modules/user/`, aliased as
493
+ `@users`):
494
+
495
+ ```text
496
+ @ → src
497
+ @users → src/modules/user
498
+ @posts → src/modules/post
499
+ @comments → src/modules/comment
500
+ ```
501
+
502
+ ```ts
503
+ import { UserService } from '@users/services/user.service';
504
+ ```
505
+
506
+ If a model's pluralized name would collide with a reserved alias (e.g. a model literally named
507
+ `Service`), generation fails with a clear error rather than silently misresolving imports.
508
+
509
+ **How aliases actually resolve at runtime** — this differs by module system, and GAZAN picks the
510
+ mechanism for you:
511
+
512
+ | Config | Dev | Build / start |
513
+ |---|---|---|
514
+ | TypeScript (CJS or MJS) | `tsx` resolves `tsconfig.json` `paths` natively | `tsc && tsc-alias` rewrites aliases to relative paths in the compiled output |
515
+ | JavaScript + CJS | `module-alias` (registered as the first line of `server.js`, reading `_moduleAliases` from `package.json`) | same |
516
+ | JavaScript + ESM (MJS) | A generated `alias-loader.mjs`, registered via `node --experimental-loader=./alias-loader.mjs` (already wired into `npm run dev` / `npm start`) | same |
517
+
518
+ A `jsconfig.json` is also generated for JS projects so editors get alias-aware intellisense — it
519
+ has no effect on how the project actually runs. If you decline aliases, none of this is
520
+ generated and every import is a plain relative path, exactly as before this feature existed.
521
+
522
+ ## Environment variables
523
+
524
+ Only variables the selected features actually need are generated, into `.env` (with working local
525
+ defaults) and `.env.example` (a template):
526
+
527
+ ```env
528
+ # Application (always)
529
+ NODE_ENV=development
530
+ PORT=3000
531
+ CORS_ORIGIN=*
532
+
533
+ # Database (only one of these, depending on selection)
534
+ DATABASE_URL=
535
+ MONGODB_URI=
536
+
537
+ # Redis (only if Redis is needed)
538
+ REDIS_URL=
539
+
540
+ # Auth — JWT / refresh tokens (only if selected)
541
+ JWT_SECRET=
542
+ JWT_EXPIRES_IN=15m
543
+ JWT_REFRESH_SECRET=
544
+ JWT_REFRESH_EXPIRES_IN=7d
545
+
546
+ # Auth — OAuth (only if selected; stub — see Authentication)
547
+ OAUTH_CLIENT_ID=
548
+ OAUTH_CLIENT_SECRET=
549
+ OAUTH_CALLBACK_URL=
550
+ ```
551
+
552
+ Validated at startup with Zod (`utils/validate-env.js`) — the app refuses to boot with a missing
553
+ or malformed required variable instead of failing confusingly later.
554
+
555
+ ## Generated project commands
556
+
557
+ ```bash
558
+ npm install
559
+ npm run dev # tsx watch (TS) or node --watch (JS)
560
+ npm run build # TypeScript only: tsc [&& tsc-alias if aliases are enabled]
561
+ npm start # node dist/server.js (TS) or node src/server.js (JS)
562
+ ```
563
+
564
+ If PostgreSQL + Prisma was selected:
565
+
566
+ ```bash
567
+ npm run db:generate # regenerate the Prisma client
568
+ npm run db:migrate # run dev migrations
569
+ npm run db:push # push schema without a migration
570
+ npm run db:studio # open Prisma Studio
571
+ ```
572
+
573
+ Every generated project exposes `GET /health` → `{ "success": true, "data": { "status": "ok" } }`,
574
+ a centralized 404 handler, and a centralized error handler.
575
+
576
+ ## Configuration examples
577
+
578
+ **A — Modern TypeScript API**: ESM, TypeScript, MVC, `src/`, PostgreSQL + Prisma, Redis, BullMQ,
579
+ Socket.IO, JWT, aliases, `entity.json`. Produces the full MVC tree shown above plus `workers/`,
580
+ `socket/`, `middlewares/auth.js`, and `prisma/schema.prisma` generated from your entities.
581
+
582
+ **B — Lightweight JavaScript API**: CommonJS, JavaScript, MVC, no `src/`, MongoDB + Mongoose, no
583
+ Redis/BullMQ/Socket.IO/auth. Produces a minimal flat tree (`controllers/`, `routes/`, `services/`,
584
+ `validators/`, `middlewares/`, `helpers/`, `utils/`, `configs/db/` at the project root) with only
585
+ `express`, `mongoose`, and the always-on security/validation dependencies.
586
+
587
+ **C — Modular backend**: ESM, TypeScript, HMVC, PostgreSQL + Prisma, aliases, `entity.json`.
588
+ Produces the HMVC tree shown above, one `@<model>` alias per entity, and per-module CRUD wired
589
+ through `src/routes/index.js`.
590
+
591
+ ## Security
592
+
593
+ - Helmet, CORS (`CORS_ORIGIN`; a startup warning fires if it's still `*` in production)
594
+ - Zod validation on every write route, before the controller runs
595
+ - Rate limiting — in-memory by default, Redis-backed (`rate-limit-redis`) when selected or when
596
+ BullMQ is enabled; fails closed (rejects requests) rather than silently going unlimited if
597
+ Redis-backed and Redis is unreachable
598
+ - bcrypt password hashing, centralized (never duplicated into controllers)
599
+ - Centralized HTTP error handling — stack traces are only ever included in a `development`
600
+ response, never in `production`
601
+ - Environment validation at startup (Zod) — required variables must be present and well-formed
602
+ - Graceful shutdown — HTTP server → Socket.IO → BullMQ → Redis → database, in order, each awaited
603
+ - Automatic stripping of password/secret/hash-like fields from generated API responses
604
+ - Generation-time safety: a non-empty target directory is never silently overwritten, HMVC module
605
+ names are validated against reserved-alias collisions, and generation happens into a temporary
606
+ directory first, only copied into place once it fully succeeds
607
+
608
+ > GAZAN generates a backend *foundation*. Application-specific security requirements — threat
609
+ > modeling, auth flow correctness, secrets management, dependency audits, and so on — still need
610
+ > to be reviewed and implemented by the project owner.
611
+
612
+ ## CLI reference
613
+
614
+ ```bash
615
+ gazan --help # command list and usage
616
+ gazan --version # installed GAZAN version
617
+ gazan init # interactively generate a new backend project
618
+ ```
619
+
620
+ There is currently no `gazan generate <resource>` (incremental regeneration into an existing
621
+ project) — see [Limitations](#project-status--limitations).
622
+
623
+ ## Generated project lifecycle
624
+
625
+ ```text
626
+ Install GAZAN
627
+
628
+ gazan init
629
+
630
+ Answer the prompts (optionally pointing at an entity.json)
631
+
632
+ GAZAN normalizes your answers into one configuration object
633
+
634
+ GAZAN generates the project (into a temp dir, then copies it into place on success)
635
+
636
+ cd <project> && npm install
637
+
638
+ Configure .env
639
+
640
+ Run migrations / database setup as appropriate (e.g. npm run db:migrate)
641
+
642
+ npm run dev
643
+ ```
644
+
645
+ GAZAN does not run migrations, seed data, or otherwise touch a live database during generation —
646
+ it only writes files.
647
+
648
+ ## Development
649
+
650
+ Working on GAZAN itself:
651
+
652
+ ```bash
653
+ git clone git@github.com:korveniq/Gazan.git
654
+ cd Gazan
655
+ npm install
656
+ npm test
657
+ node bin/gazan.js init # run the generator locally against a scratch directory
658
+ ```
659
+
660
+ ## Testing
661
+
662
+ `npm test` runs `tests/run.js`, an end-to-end suite that generates real projects (into
663
+ `.test-scratch/`, gitignored) and validates them with the actual toolchain — `npm install`,
664
+ `tsc --noEmit`, `prisma validate`/`prisma generate`, and for a representative subset, actually
665
+ booting the server and checking `/health` plus the absence of module-resolution errors — not just
666
+ static inspection of the generated text.
667
+
668
+ The matrix covers, in combination: CJS/MJS, JS/TS, MVC/HMVC, `src`/no-`src`, PostgreSQL+Prisma,
669
+ MongoDB+Mongoose, MongoDB (native), no database, Redis (with and without BullMQ), BullMQ,
670
+ Socket.IO, each authentication method in isolation, module aliases (across every module
671
+ system/language combination, MVC and HMVC), and `entity.json` schemas including self-relations,
672
+ dual foreign keys to the same model, many-to-many, and a six-model realistic schema.
673
+
674
+ ## Troubleshooting
675
+
676
+ **Node.js version** — GAZAN requires Node 18+ (see `engines` in `package.json`) and checks this
677
+ itself at startup, exiting with a clear message on older versions.
678
+
679
+ **Permission errors installing globally** — prefer `npx gazan-init init` over
680
+ `npm install -g gazan-init` if you hit `EACCES` or similar; it avoids global install
681
+ permissions entirely.
682
+
683
+ **`entity.json` errors** — invalid entity definitions are rejected with field-level validation
684
+ errors (see [entity.json](#entityjson)) before any files are generated, not partway through.
685
+
686
+ **Database connection during generation** — generation itself never requires a live database;
687
+ it only produces schema/config files. `npm run db:migrate` (Prisma) or connecting your generated
688
+ app does.
689
+
690
+ **Existing directories** — if the target directory has content in it, GAZAN asks you to cancel,
691
+ continue (existing files may be overwritten), or choose another directory. A directory containing
692
+ only a stray `.git` is still treated as empty.
693
+
694
+ ## Project status / limitations
695
+
696
+ - **OAuth is a stub** (env vars + a documented `throw`, not a working provider integration).
697
+ - **Many-to-many CRUD writes aren't automatic** — the relation is schema-correct but generated
698
+ services don't read/write it (see [Relations](#relations)).
699
+ - **No incremental `gazan generate model`** — only full `gazan init`.
700
+ - **No live-database round-trip tests** in this repository's own test suite (no Postgres/Mongo/
701
+ Redis instance in CI/sandbox) — tests verify schema validity, compilation, and that the app
702
+ boots and fails at the *expected* connection boundary, not full data round-trips.
703
+ - **Composite (multi-field) unique constraints/indexes** aren't representable in `entity.json`'s
704
+ current per-field-only shape.
705
+ - entity.json has no per-model way to require authentication on generated routes — wire
706
+ `middlewares/auth.js` into a generated route file yourself if you need that.
707
+
708
+ ## Contributing
709
+
710
+ Issues and pull requests are welcome. Before opening a PR:
711
+
712
+ ```bash
713
+ npm test
714
+ ```
715
+
716
+ Keep changes scoped — GAZAN's generators are composable and conditional by design; a new feature
717
+ should follow the same pattern (only generate what's selected, single source of truth for shared
718
+ config, no duplicated logic across CJS/MJS/JS/TS variants).
719
+
720
+ ## License
721
+
722
+ [MIT](./LICENSE)