langaro-api 1.2.3 → 1.2.5

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.
@@ -0,0 +1,344 @@
1
+ # CLI & Scaffolding
2
+
3
+ ## Overview
4
+
5
+ The `langaro-api` CLI provides commands for project scaffolding, resource creation, type generation, and project migration.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ `langaro-api` is installed as a project dependency. The CLI is available via `npx`:
12
+
13
+ ```bash
14
+ npx langaro-api # Interactive menu (or auto-generate types in CI)
15
+ npx langaro-api init # Scaffold new project
16
+ npx langaro-api new # Create new resource
17
+ npx langaro-api generate # Generate TypeScript types
18
+ npx langaro-api --watch # Watch mode for type generation
19
+ npx langaro-api migrate # Migrate existing project to use loaders
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Interactive Menu
25
+
26
+ Running `npx langaro-api` without arguments in a TTY terminal shows an interactive menu:
27
+
28
+ ```
29
+ > Generate types
30
+ New resource
31
+ Migrate project
32
+ Init new project
33
+ ```
34
+
35
+ Navigate with arrow keys, select with Enter.
36
+
37
+ In a non-TTY environment (CI/CD), it auto-runs type generation.
38
+
39
+ ---
40
+
41
+ ## `langaro-api init`
42
+
43
+ Scaffolds a complete project from scratch.
44
+
45
+ ### Usage
46
+
47
+ ```bash
48
+ mkdir my-project && cd my-project
49
+ npx langaro-api init
50
+ ```
51
+
52
+ ### Prompts
53
+
54
+ | Prompt | Default | Description |
55
+ |--------|---------|-------------|
56
+ | Project name | folder name | Used in `package.json` |
57
+ | Port | `8282` | Server listen port |
58
+ | Database name | project name (underscored) | MySQL database |
59
+ | Redis port | `6379` | Redis connection port |
60
+
61
+ ### Files Created
62
+
63
+ **Root config:**
64
+ - `package.json` — All dependencies pre-configured
65
+ - `.babelrc` — Babel with `@` path alias
66
+ - `jsconfig.json` — IDE path resolution
67
+ - `.eslintrc.js` — Airbnb-base ESLint config
68
+ - `.eslintignore`
69
+ - `jest.config.js` — Jest test configuration
70
+ - `.gitignore` — Standard ignores
71
+ - `.env` — Environment variables (with auto-generated JWT_SECRET and MASTER_PASS)
72
+ - `.env.example` — Template without secrets
73
+
74
+ **Application:**
75
+ - `src/config/server.js` — Server entry point
76
+ - `src/config/app.js` — App class (middleware, routes, errors)
77
+ - `src/config/queues.js` — BullMQ queue system
78
+ - `src/config/instrument.js` — Sentry setup
79
+ - `src/config/tests/global-setup-tests.js`
80
+ - `src/config/tests/setup-tests.js`
81
+
82
+ **Database:**
83
+ - `src/database/connection.js` — Knex MySQL connection
84
+ - `src/database/redis-config.js` — Redis connection params
85
+ - `src/database/redis-cache.js` — RedisCache class
86
+
87
+ **Loaders (index.js files):**
88
+ - `src/database/models/index.js`
89
+ - `src/database/services/index.js`
90
+ - `src/controllers/index.js`
91
+ - `src/routes/index.js`
92
+ - `src/jobs/index.js`
93
+ - `src/tasks/index.js`
94
+
95
+ **Auth:**
96
+ - `src/middlewares/auth.middleware.js` — JWT authentication
97
+
98
+ **Utils:**
99
+ - `src/utils/index.js` — Re-exports common packages
100
+ - `src/utils/sleep.js` — Sleep utility
101
+
102
+ **Empty directories:**
103
+ - `src/integrations/`
104
+ - `src/static/`
105
+ - `src/temp/`
106
+ - `src/database/tests/`
107
+
108
+ **Documentation:**
109
+ - `documentation/` — Architecture documentation (this folder)
110
+
111
+ ### Behavior
112
+
113
+ - **Skips existing files** — Running `init` on an existing project only creates files that don't exist yet. Safe to run on existing projects to add missing scaffolding.
114
+ - Auto-generates `JWT_SECRET` (UUID) and `MASTER_PASS` (random string) in `.env`
115
+
116
+ ### Next Steps After Init
117
+
118
+ ```bash
119
+ npm install
120
+ # Fill in .env (DB_PASSWORD, etc.)
121
+ # Create the database in MySQL
122
+ npm run dev
123
+ npx langaro-api new # Create your first resource
124
+ ```
125
+
126
+ ---
127
+
128
+ ## `langaro-api new`
129
+
130
+ Scaffolds a new resource (controller, service, model, route).
131
+
132
+ ### Usage
133
+
134
+ ```bash
135
+ npx langaro-api new
136
+ ```
137
+
138
+ ### Steps
139
+
140
+ 1. **Choose naming source:**
141
+ - **Enter manually** — Type a snake_case name
142
+ - **Fetch from database** — Select from existing MySQL tables
143
+
144
+ 2. **Select what to generate:**
145
+ - [x] Controller
146
+ - [x] Service
147
+ - [x] Model
148
+ - [x] Route
149
+
150
+ Toggle with Space, confirm with Enter.
151
+
152
+ 3. **Files created** (for resource `products`):
153
+ - `src/controllers/products/products.controller.js`
154
+ - `src/database/services/products.services.js`
155
+ - `src/database/models/products.model.js`
156
+ - `src/routes/products.router.js`
157
+
158
+ 4. **Types auto-regenerated** after creation.
159
+
160
+ ### Templates Created
161
+
162
+ **Controller:**
163
+ ```javascript
164
+ /** @param {typeof ProductsControllerBase} ServicesClass @generated-types */
165
+ module.exports = (ServicesClass) => class extends ServicesClass {
166
+ async getAll(req, res) {
167
+ const data = await this.service.get();
168
+ res.status(200).json(data);
169
+ }
170
+ };
171
+ ```
172
+
173
+ **Service:**
174
+ ```javascript
175
+ /** @param {typeof ProductsServicesBase} model @param {ModelsConstructorMap} models @generated-types */
176
+ module.exports = (model, models) => class extends model {
177
+ async defaultMethod(something) {
178
+ const ProductsModel = new model();
179
+ return something;
180
+ }
181
+ };
182
+ ```
183
+
184
+ **Model:**
185
+ ```javascript
186
+ /** @type {ModelConfig} @generated-types */
187
+ module.exports = {
188
+ fields: {},
189
+ sortBy: 'created_at',
190
+ };
191
+ ```
192
+
193
+ **Route:**
194
+ ```javascript
195
+ const createAuthMiddleware = require('@/middlewares/auth.middleware');
196
+
197
+ /** @param {ControllersMap} controllers @param {ServicesMap} services @generated-types */
198
+ module.exports = (controllers, services) => {
199
+ const auth = createAuthMiddleware(services);
200
+ const router = require('express').Router();
201
+ const controller = controllers.ProductsController;
202
+ const subject = 'products';
203
+
204
+ router.get('/', auth([{ subject }]), controller.getAll.bind(controller));
205
+
206
+ return router;
207
+ };
208
+ ```
209
+
210
+ ### Resource Naming
211
+
212
+ - Must be `snake_case`: `products`, `user_invoices`, `crm_leads`
213
+ - Must start with a lowercase letter
214
+ - Validated with: `/^[a-z][a-z0-9_]*$/`
215
+
216
+ ---
217
+
218
+ ## `langaro-api generate` / `langaro-api types`
219
+
220
+ Generates TypeScript definition files and injects JSDoc annotations for IDE IntelliSense.
221
+
222
+ ### Output
223
+
224
+ ```
225
+ @types/generated/
226
+ ├── crud.d.ts # CRUD class + option interfaces
227
+ ├── services.d.ts # Service interfaces + ServicesMap
228
+ ├── controllers.d.ts # Controller base classes + ControllersMap
229
+ ├── index.d.ts # Reference includes
230
+ ├── crud.d.ts.map # Source maps for Go-to-Definition
231
+ ├── services.d.ts.map
232
+ └── controllers.d.ts.map
233
+ ```
234
+
235
+ ### JSDoc Injection
236
+
237
+ The generator also injects/updates `@generated-types` JSDoc comments in source files:
238
+
239
+ ```javascript
240
+ // Before:
241
+ module.exports = (ServicesClass) => class extends ServicesClass { ... }
242
+
243
+ // After:
244
+ /** @param {typeof UsersControllerBase} ServicesClass @generated-types */
245
+ module.exports = (ServicesClass) => class extends ServicesClass { ... }
246
+ ```
247
+
248
+ This enables IDE autocomplete for `this.service`, `this.services`, `controllers.*`, etc.
249
+
250
+ ### When to Run
251
+
252
+ - After creating new component files
253
+ - After renaming or deleting components
254
+ - After `npm install` (if knex-extended-crud updated)
255
+ - Automatically via `npm run dev` (watch mode)
256
+
257
+ ---
258
+
259
+ ## `langaro-api --watch`
260
+
261
+ Continuous type generation that watches source directories:
262
+
263
+ ```bash
264
+ npx langaro-api --watch
265
+ ```
266
+
267
+ Watches: `src/database/models/`, `src/database/services/`, `src/controllers/`, `src/routes/`, `src/jobs/`, `src/tasks/`, `src/middlewares/`
268
+
269
+ Debounce: 300ms
270
+
271
+ Usually run via `npm run dev` which uses `npm-run-all -p dev:server dev:types`.
272
+
273
+ ---
274
+
275
+ ## `langaro-api migrate`
276
+
277
+ Converts an existing project to use langaro-api loaders by replacing hand-written `index.js` files.
278
+
279
+ ### What It Does
280
+
281
+ Replaces these files with loader calls:
282
+ - `src/database/models/index.js` → `loadModels`
283
+ - `src/database/services/index.js` → `loadServices`
284
+ - `src/controllers/index.js` → `loadControllers`
285
+ - `src/routes/index.js` → `attachRouters`
286
+ - `src/jobs/index.js` → `loadJobs`
287
+ - `src/tasks/index.js` → `loadTasks`
288
+
289
+ Creates `.bak` backups of original files.
290
+
291
+ ---
292
+
293
+ ## Configuration File
294
+
295
+ Optional `langaro-api.config.js` in project root to override default paths:
296
+
297
+ ```javascript
298
+ module.exports = {
299
+ root: process.cwd(),
300
+ output: '@types/generated',
301
+ services: 'src/database/services',
302
+ models: 'src/database/models',
303
+ controllers: 'src/controllers',
304
+ routes: 'src/routes',
305
+ jobs: 'src/jobs',
306
+ tasks: 'src/tasks',
307
+ middlewares: 'src/middlewares',
308
+ };
309
+ ```
310
+
311
+ Only needed if your project uses non-standard directory names.
312
+
313
+ ---
314
+
315
+ ## Development Workflow
316
+
317
+ ```bash
318
+ # Start development (server + type generation)
319
+ npm run dev
320
+
321
+ # Create a new resource
322
+ npx langaro-api new
323
+
324
+ # Manually regenerate types
325
+ npm run generate-types
326
+
327
+ # Run tests
328
+ npm test
329
+ ```
330
+
331
+ ### npm Scripts (from init)
332
+
333
+ ```json
334
+ {
335
+ "start": "babel-node ./src/config/server.js",
336
+ "dev": "npm-run-all -p dev:*",
337
+ "dev:server": "nodemon --exec babel-node ./src/config/server.js",
338
+ "dev:types": "langaro-api --watch",
339
+ "generate-types": "langaro-api",
340
+ "test": "jest",
341
+ "test:watch": "jest --watchAll",
342
+ "test:coverage": "jest --coverage"
343
+ }
344
+ ```
@@ -0,0 +1,116 @@
1
+ # Langaro API — Architecture Guide
2
+
3
+ This project uses the **Langaro API** framework: a convention-over-configuration Node.js framework built on Express 5, Knex (MySQL), BullMQ, Redis, and Socket.io. Database operations are powered by **knex-extended-crud**, which provides a full-featured CRUD class with validation, caching, relationships, and batch operations.
4
+
5
+ ---
6
+
7
+ ## Quick Decision Tree
8
+
9
+ | I need to... | Read |
10
+ |-------------|------|
11
+ | Understand the full architecture | `01-architecture-overview.md` |
12
+ | Look up a CRUD method (get, create, update, etc.) | `02-crud-layer.md` |
13
+ | Define field visibility, validation, or relationships for a table | `03-models.md` |
14
+ | Add business logic for a table | `04-services.md` |
15
+ | Add an API endpoint | `05-controllers.md` + `06-routes.md` |
16
+ | Add a background job | `07-jobs.md` + `12-queues.md` |
17
+ | Add a scheduled task | `08-tasks.md` |
18
+ | Add or modify authentication/authorization | `09-middlewares.md` |
19
+ | Connect to an external API | `10-integrations.md` |
20
+ | Modify server startup, database, or Redis config | `11-config-and-bootstrap.md` |
21
+ | Understand the queue system | `12-queues.md` |
22
+ | Add a utility function | `13-utils.md` |
23
+ | Write tests | `14-testing.md` |
24
+ | Scaffold a new resource or project | `15-cli-and-scaffolding.md` |
25
+
26
+ ---
27
+
28
+ ## Adding a New Database-Backed Resource (end-to-end)
29
+
30
+ 1. Create the MySQL table (migration)
31
+ 2. Create the model: `src/database/models/{table}.model.js` → `03-models.md`
32
+ 3. Create the service: `src/database/services/{table}.services.js` → `04-services.md`
33
+ 4. Create the controller: `src/controllers/{table}/{table}.controller.js` → `05-controllers.md`
34
+ 5. Create the route: `src/routes/{table}.router.js` → `06-routes.md`
35
+ 6. Regenerate types: `npx langaro-api generate` → `15-cli-and-scaffolding.md`
36
+
37
+ Or use the CLI shortcut: `npx langaro-api new`
38
+
39
+ ---
40
+
41
+ ## Document Index
42
+
43
+ | # | Document | Description |
44
+ |---|----------|-------------|
45
+ | 01 | `01-architecture-overview.md` | Three-layer stack, boot sequence, inheritance chain, directory structure |
46
+ | 02 | `02-crud-layer.md` | knex-extended-crud API: get, create, update, delete, search, validation, caching |
47
+ | 03 | `03-models.md` | Model configuration: fields, hide, append, schema, filterOptions |
48
+ | 04 | `04-services.md` | Business logic layer: factory pattern, transactions, Socket.io |
49
+ | 05 | `05-controllers.md` | Request handlers: ServicesClass, validation, Queue, response patterns |
50
+ | 06 | `06-routes.md` | Express routers: URL mapping, auth middleware, binding |
51
+ | 07 | `07-jobs.md` | BullMQ async jobs: handle, retry, grouped queues, error handling |
52
+ | 08 | `08-tasks.md` | Cron scheduled tasks: cronTime, environments, patterns |
53
+ | 09 | `09-middlewares.md` | Authentication, authorization, permission system |
54
+ | 10 | `10-integrations.md` | External service wrappers: Stripe, AWS, email, etc. |
55
+ | 11 | `11-config-and-bootstrap.md` | Server, app, database, Redis, environment variables |
56
+ | 12 | `12-queues.md` | Queue infrastructure: lifecycle, monitoring, shutdown |
57
+ | 13 | `13-utils.md` | Utility functions: validation, formatting, common helpers |
58
+ | 14 | `14-testing.md` | Jest config, test database, queue mocking, patterns |
59
+ | 15 | `15-cli-and-scaffolding.md` | CLI commands: init, new, generate, watch, migrate |
60
+
61
+ ---
62
+
63
+ ## Architecture Stack
64
+
65
+ ```
66
+ ┌──────────────────────────────────┐
67
+ │ PROJECT CODE │
68
+ │ (this repository) │
69
+ ├──────────────────────────────────┤
70
+ │ LANGARO-API │
71
+ │ (loaders, CLI, types) │
72
+ ├──────────────────────────────────┤
73
+ │ KNEX-EXTENDED-CRUD │
74
+ │ (CRUD, validation, cache) │
75
+ ├──────────────────────────────────┤
76
+ │ Express + Knex/MySQL + Redis │
77
+ │ BullMQ + Socket.io + Sentry │
78
+ └──────────────────────────────────┘
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Naming Conventions
84
+
85
+ | Component | File Pattern | Instance Key |
86
+ |-----------|-------------|-------------|
87
+ | Model | `{table}.model.js` | — |
88
+ | Service | `{table}.services.js` | `PascalCaseServices` |
89
+ | Controller | `{table}/{table}.controller.js` | `PascalCaseController` |
90
+ | Route | `{table}.router.js` | URL: `/kebab-case` |
91
+ | Job | `{name}.js` | Queue name: `kebab-case` |
92
+ | Task | `{name}.js` | — |
93
+ | Middleware | `{name}.middleware.js` | — |
94
+ | Integration | `{name}.js` | — |
95
+
96
+ **Tables** use `snake_case` matching the MySQL table name.
97
+
98
+ ---
99
+
100
+ ## Key Terminology
101
+
102
+ | Term | Definition |
103
+ |------|-----------|
104
+ | **CRUD** | The base class from knex-extended-crud. All models and services inherit from it. |
105
+ | **Model** | A configuration object (not a class) that shapes CRUD behavior for a table. |
106
+ | **Service** | A class extending CRUD with custom business logic methods. |
107
+ | **ServicesClass** | The base class injected into controllers with `this.service`, `this.services`, `this.Queue`, `this.io`. |
108
+ | **Controller** | A class extending ServicesClass that handles HTTP requests. |
109
+ | **Loader** | A framework function that auto-discovers and instantiates components from directories. |
110
+ | **Factory Function** | The export pattern used by services, controllers, routes, jobs, and tasks — a function that receives dependencies and returns a class or object. |
111
+ | **Queue** | The BullMQ job queue interface: `{ add(name, data, options) }`. |
112
+ | **Task** | A cron-scheduled function that runs automatically at defined intervals. |
113
+ | **append** | The relationship system that loads related data from other tables. |
114
+ | **andWhere** | A declarative array-based query condition builder used in CRUD methods. |
115
+ | **Safe Error** | An error created via `ApiError()` that returns a specific HTTP status and message to the client. |
116
+ | **Unsafe Error** | Any uncaught error — returns generic 500 and is captured by Sentry. |
package/lib/cli/init.js CHANGED
@@ -1106,15 +1106,43 @@ async function run() {
1106
1106
  createEmptyDir(root, dir);
1107
1107
  });
1108
1108
 
1109
+ // documentation/ architecture documentation
1110
+ const cloudTemplatesDir = path.join(__dirname, 'documentation-templates');
1111
+ if (fs.existsSync(cloudTemplatesDir)) {
1112
+ fs.readdirSync(cloudTemplatesDir)
1113
+ .filter((f) => f.endsWith('.md'))
1114
+ .sort()
1115
+ .forEach((file) => {
1116
+ const content = fs.readFileSync(path.join(cloudTemplatesDir, file), 'utf8');
1117
+ writeFile(root, `documentation/${file}`, content);
1118
+ });
1119
+ }
1120
+
1109
1121
  console.log(`\n\x1b[32mProject scaffolded!\x1b[0m\n`);
1110
1122
  console.log('Next steps:');
1111
1123
  console.log(` 1. \x1b[36mnpm install\x1b[0m`);
1112
1124
  console.log(` 2. Fill in your \x1b[36m.env\x1b[0m (DB_PASSWORD, etc.)`);
1113
1125
  console.log(` 3. Create the \x1b[36m${dbName}\x1b[0m database in MySQL`);
1114
1126
  console.log(` 4. \x1b[36mnpm run dev\x1b[0m`);
1115
- console.log(` 5. \x1b[36mnpx langaro-api new\x1b[0m to create your first resource\n`);
1127
+ console.log(` 5. \x1b[36mnpx langaro-api new\x1b[0m to create your first resource`);
1128
+ console.log(` 6. Review \x1b[36mdocumentation/\x1b[0m for architecture documentation\n`);
1116
1129
 
1117
1130
  process.exit(0);
1118
1131
  }
1119
1132
 
1120
- module.exports = run;
1133
+ function updateDocumentation(root) {
1134
+ const templatesDir = path.join(__dirname, 'documentation-templates');
1135
+ if (fs.existsSync(templatesDir)) {
1136
+ fs.readdirSync(templatesDir)
1137
+ .filter((f) => f.endsWith('.md'))
1138
+ .sort()
1139
+ .forEach((file) => {
1140
+ const full = path.join(root, `documentation/${file}`);
1141
+ fs.mkdirSync(path.dirname(full), { recursive: true });
1142
+ fs.writeFileSync(full, fs.readFileSync(path.join(templatesDir, file), 'utf8'));
1143
+ console.log(` \x1b[32mcreated\x1b[0m documentation/${file}`);
1144
+ });
1145
+ }
1146
+ }
1147
+
1148
+ module.exports = { run, updateDocumentation };
@@ -95,6 +95,11 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
95
95
  lines.push(' count(options?: CRUDGetOptions, transaction?: any): Promise<number>;');
96
96
  lines.push('');
97
97
 
98
+ addMethodMapping(lines, 'atomicIncrementWhere');
99
+ lines.push(' atomicIncrementWhere(');
100
+ lines.push(' prop: string, value: any, increments: Record<string, number>,');
101
+ lines.push(' options?: CRUDUpdateOptions, transaction?: any');
102
+ lines.push(' ): Promise<{ success: boolean; data: any }>;');
98
103
  addMethodMapping(lines, 'updateWhere');
99
104
  lines.push(' updateWhere(');
100
105
  lines.push(' prop: string, value: any, data?: Record<string, any>,');
@@ -171,6 +176,8 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
171
176
  ' sortBy?: string;',
172
177
  " sort?: 'asc' | 'desc';",
173
178
  ' where?: (query: any) => any;',
179
+ ' /** Lock the selected rows for update within a transaction (default: false) */',
180
+ ' lockRow?: boolean;',
174
181
  ' /** Attempt to read from Redis cache (default: false) */',
175
182
  ' cache?: boolean;',
176
183
  ' /** Write result to Redis cache with this TTL in hours */',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langaro-api",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Auto-generate TypeScript types, JSDoc annotations, and boilerplate loaders for knex-extended-crud projects",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "peerDependencies": {
24
24
  "cron": ">=3.0.0",
25
- "knex-extended-crud": ">=2.1.2"
25
+ "knex-extended-crud": ">=2.1.3"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "cron": {