@avleon/cli 0.0.45 → 0.0.47

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/dist/create.js CHANGED
@@ -147,7 +147,8 @@ async function askForFeatures() {
147
147
  choices: [
148
148
  { name: 'OpenAPI / Swagger docs', value: 'openapi', checked: true },
149
149
  { name: 'CORS support', value: 'cors', checked: false },
150
- { name: 'TypeORM database', value: 'typeorm', checked: false },
150
+ { name: 'TypeORM', value: 'typeorm', checked: false },
151
+ { name: 'Knex', value: 'knex', checked: false },
151
152
  { name: 'Scalar UI (replaces Swagger UI)', value: 'scalar', checked: false },
152
153
  ],
153
154
  },
@@ -187,83 +188,84 @@ async function writeFile(filePath, content, label) {
187
188
  }
188
189
  // ─── Templates ────────────────────────────────────────────────────────────────
189
190
  function homeServiceTemplate() {
190
- return `
191
- import { AppService } from '@avleon/core';
192
-
193
- @AppService
194
- export class HomeService {
195
- sayHello() {
196
- return { message: 'Hello World!' };
197
- }
198
- }
191
+ return `
192
+ import { AppService } from '@avleon/core';
193
+
194
+ @AppService
195
+ export class HomeService {
196
+ sayHello() {
197
+ return { message: 'Hello World!' };
198
+ }
199
+ }
199
200
  `;
200
201
  }
201
202
  function homeControllerTemplate() {
202
- return `
203
- import { ApiController, Get } from '@avleon/core';
204
- import { HomeService } from '../services/home.service';
205
-
206
- @ApiController('/')
207
- export class HomeController {
208
- constructor(private readonly homeService: HomeService) {}
209
-
210
- @Get()
211
- sayHello() {
212
- return this.homeService.sayHello();
213
- }
214
- }
203
+ return `
204
+ import { ApiController, Get } from '@avleon/core';
205
+ import { HomeService } from '../services/home.service';
206
+
207
+ @ApiController('/')
208
+ export class HomeController {
209
+ constructor(private readonly homeService: HomeService) {}
210
+
211
+ @Get()
212
+ sayHello() {
213
+ return this.homeService.sayHello();
214
+ }
215
+ }
215
216
  `;
216
217
  }
217
218
  function homeControllerTestTemplate() {
218
- return `
219
- import { AvleonTest } from '@avleon/core';
220
- import { HomeController } from './home.controller';
221
-
222
- describe('HomeController', () => {
223
- let controller!: HomeController;
224
-
225
- beforeAll(() => {
226
- controller = AvleonTest.getController(HomeController);
227
- });
228
-
229
- it('should be defined', () => {
230
- expect(controller).toBeDefined();
231
- });
232
-
233
- it('should have sayHello method', () => {
234
- expect(typeof controller.sayHello).toBe('function');
235
- });
236
-
237
- it('should return hello message', () => {
238
- expect(controller.sayHello()).toEqual({ message: 'Hello World!' });
239
- });
240
- });
219
+ return `
220
+ import { AvleonTest } from '@avleon/core';
221
+ import { HomeController } from './home.controller';
222
+
223
+ describe('HomeController', () => {
224
+ let controller!: HomeController;
225
+
226
+ beforeAll(() => {
227
+ controller = AvleonTest.getController(HomeController);
228
+ });
229
+
230
+ it('should be defined', () => {
231
+ expect(controller).toBeDefined();
232
+ });
233
+
234
+ it('should have sayHello method', () => {
235
+ expect(typeof controller.sayHello).toBe('function');
236
+ });
237
+
238
+ it('should return hello message', () => {
239
+ expect(controller.sayHello()).toEqual({ message: 'Hello World!' });
240
+ });
241
+ });
241
242
  `;
242
243
  }
243
244
  function openApiConfigTemplate(fname, useScalar) {
244
- return `
245
- import { AppConfig, Environment, IConfig } from '@avleon/core';
246
-
247
- @AppConfig
248
- export class OpenApiConfig implements IConfig {
249
- config(env: Environment) {
250
- return {
251
- info: {
252
- title: '${(0, lodash_1.capitalize)(fname)} API',
253
- version: '0.0.1',
254
- description: 'API documentation for ${(0, lodash_1.capitalize)(fname)}',
255
- },
256
- ${useScalar ? "provider: 'scalar'," : ''}
257
- routePrefix: '/docs',
258
- };
259
- }
260
- }
245
+ return `
246
+ import { AppConfig, AvleonConfig, Environment, OpenApiUiOptions } from '@avleon/core';
247
+
248
+ @AppConfig
249
+ export class OpenApiConfig extends AvleonConfig<OpenApiUiOptions> {
250
+ config(env: Environment): OpenApiUiOptions {
251
+ return {
252
+ info: {
253
+ title: '${(0, lodash_1.capitalize)(fname)} API',
254
+ version: '0.0.1',
255
+ description: 'API documentation for ${(0, lodash_1.capitalize)(fname)}',
256
+ },
257
+ ${useScalar ? "provider: 'scalar'," : ''}
258
+ routePrefix: '/docs',
259
+ };
260
+ }
261
+ }
261
262
  `;
262
263
  }
263
264
  function appTemplate(features) {
264
265
  const hasOpenApi = features.includes('openapi');
265
266
  const hasCors = features.includes('cors');
266
267
  const hasTypeorm = features.includes('typeorm');
268
+ const hasKnex = features.includes('knex');
267
269
  const imports = [`import { Avleon } from '@avleon/core';`];
268
270
  imports.push(`import { HomeController } from './controllers/home.controller';`);
269
271
  if (hasOpenApi) {
@@ -280,8 +282,13 @@ function appTemplate(features) {
280
282
  body.push('');
281
283
  }
282
284
  if (hasTypeorm) {
283
- body.push(`// TODO: configure datasource in src/config/database.config.ts`);
284
- body.push(`// app.useDatasource(AppDataSource);`);
285
+ body.push(`// TODO: pass your TypeORM DataSourceOptions (or an @AppConfig class)`);
286
+ body.push(`// await app.useTypeORM({ type: 'postgres', url: process.env.DATABASE_URL, entities: [] });`);
287
+ body.push('');
288
+ }
289
+ if (hasKnex) {
290
+ body.push(`// TODO: pass your Knex config (or an @AppConfig class)`);
291
+ body.push(`// await app.useKnex({ client: 'pg', connection: process.env.DATABASE_URL });`);
285
292
  body.push('');
286
293
  }
287
294
  body.push(`app.useControllers([HomeController]);`);
@@ -290,14 +297,17 @@ function appTemplate(features) {
290
297
  return `${imports.join('\n')}\n\n${body.join('\n')}\n`;
291
298
  }
292
299
  function serveTemplate() {
293
- return `
294
- import app from './app';
295
-
296
- async function start() {
297
- await app.run(); // default port: 4000
298
- }
299
-
300
- start();
300
+ return `
301
+ import { Environment, inject } from '@avleon/core';
302
+ import app from './app';
303
+
304
+ async function start() {
305
+ const env = inject(Environment);
306
+ const port = env.get<number>('PORT') || 4000;
307
+ await app.run(port);
308
+ }
309
+
310
+ start();
301
311
  `;
302
312
  }
303
313
  function packageJsonTemplate(fname, features) {
@@ -318,6 +328,10 @@ function packageJsonTemplate(fname, features) {
318
328
  deps['typeorm'] = resolveDepVersion('typeorm', '^0.3.20');
319
329
  deps['pg'] = resolveDepVersion('pg', '^8.11.0');
320
330
  }
331
+ if (features.includes('knex')) {
332
+ deps['knex'] = resolveDepVersion('knex', '^3.1.0');
333
+ deps['pg'] = resolveDepVersion('pg', '^8.11.0');
334
+ }
321
335
  if (features.includes('scalar')) {
322
336
  deps['@scalar/fastify-api-reference'] = resolveDepVersion('@scalar/fastify-api-reference', '^1.0.0');
323
337
  }
@@ -399,35 +413,35 @@ function envTemplate() {
399
413
  }
400
414
  function readmeTemplate(fname, pm) {
401
415
  const runCmd = pm === 'npm' ? 'npm run start:dev' : `${pm} start:dev`;
402
- return `# ${(0, lodash_1.capitalize)(fname)}
403
-
404
- Built with [Avleon](https://github.com/xtareq/avleon).
405
-
406
- ## Getting Started
407
-
408
- \`\`\`bash
409
- ${runCmd}
410
- \`\`\`
411
-
412
- ## Scripts
413
-
414
- | Script | Description |
415
- |--------|-------------|
416
- | \`start:dev\` | Start development server with hot reload |
417
- | \`build\` | Compile TypeScript to JavaScript |
418
- | \`start\` | Start production server |
419
- | \`test\` | Run unit tests |
420
-
421
- ## Project Structure
422
-
423
- \`\`\`
424
- src/
425
- ├── controllers/ # Route controllers
426
- ├── services/ # Business logic
427
- ├── config/ # App configuration
428
- ├── app.ts # App setup
429
- └── serve.ts # Entry point
430
- \`\`\`
416
+ return `# ${(0, lodash_1.capitalize)(fname)}
417
+
418
+ Built with [Avleon](https://github.com/xtareq/avleon).
419
+
420
+ ## Getting Started
421
+
422
+ \`\`\`bash
423
+ ${runCmd}
424
+ \`\`\`
425
+
426
+ ## Scripts
427
+
428
+ | Script | Description |
429
+ |--------|-------------|
430
+ | \`start:dev\` | Start development server with hot reload |
431
+ | \`build\` | Compile TypeScript to JavaScript |
432
+ | \`start\` | Start production server |
433
+ | \`test\` | Run unit tests |
434
+
435
+ ## Project Structure
436
+
437
+ \`\`\`
438
+ src/
439
+ ├── controllers/ # Route controllers
440
+ ├── services/ # Business logic
441
+ ├── config/ # App configuration
442
+ ├── app.ts # App setup
443
+ └── serve.ts # Entry point
444
+ \`\`\`
431
445
  `;
432
446
  }
433
447
  // ─── Main ─────────────────────────────────────────────────────────────────────
@@ -5,9 +5,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.pathHelper = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
- // export const __filename = fileURLToPath(process.argv[1])
9
- // export const __dirname = path.dirname(__filename)
10
- /* */
8
+ const fs_1 = require("fs");
9
+ /**
10
+ * Resolve the package root (the directory holding `stubs/`) by walking up from
11
+ * this file. Compiled output lives under `dist/`, at a depth that differs per
12
+ * file, so anchoring on a fixed number of `..` segments is not reliable.
13
+ */
14
+ function packageRoot() {
15
+ let dir = __dirname;
16
+ for (let i = 0; i < 5; i++) {
17
+ if ((0, fs_1.existsSync)(path_1.default.join(dir, 'stubs')))
18
+ return dir;
19
+ const parent = path_1.default.dirname(dir);
20
+ if (parent === dir)
21
+ break;
22
+ dir = parent;
23
+ }
24
+ return path_1.default.join(__dirname, '../..');
25
+ }
11
26
  exports.pathHelper = {
12
27
  appSoucePath(p) {
13
28
  return path_1.default.join(process.cwd(), p ? './src/' + p : './src/');
@@ -16,6 +31,6 @@ exports.pathHelper = {
16
31
  return path_1.default.join(process.cwd(), p ? './test/' + p : './test/');
17
32
  },
18
33
  stubPath(p) {
19
- return path_1.default.join(__dirname, p ? '../../stubs/' + p : '../../stubs/');
34
+ return path_1.default.join(packageRoot(), 'stubs', p ?? '');
20
35
  },
21
36
  };
package/dist/service.js CHANGED
@@ -20,10 +20,10 @@ function lower(str) {
20
20
  }
21
21
  const servicesStubs = {
22
22
  basic: path_helper_1.pathHelper.stubPath('services/basic-service.stub'),
23
- rest: path_helper_1.pathHelper.stubPath('services/basic-service.stub'),
23
+ rest: path_helper_1.pathHelper.stubPath('services/rest-service.stub'),
24
24
  ormRest: path_helper_1.pathHelper.stubPath('services/orm-rest-service.stub')
25
25
  };
26
- async function createService(name, options = { force: false, resource: false, prefix: true, rest: false, model: null, root: null }, p) {
26
+ async function createService(name, options = { force: false, resource: false, prefix: true, rest: false, model: null, root: null, orm: false }, p) {
27
27
  if (!name) {
28
28
  throw new Error("Service name not found. use --name or npx avleon make:service HomeService");
29
29
  }
@@ -32,17 +32,13 @@ async function createService(name, options = { force: false, resource: false, pr
32
32
  }
33
33
  // determine stub for generate
34
34
  let stubPath = servicesStubs.basic;
35
- if (options.rest) {
36
- stubPath = servicesStubs.rest;
37
- if (options.model) {
38
- stubPath = servicesStubs.ormRest;
39
- }
35
+ if (options.rest || options.resource) {
36
+ stubPath = options.orm ? servicesStubs.ormRest : servicesStubs.rest;
40
37
  }
41
38
  const rootPath = options.root ? 'src/' + options.root + '/' : 'src/services/';
42
- if (name.split("/").length > 1) {
43
- if (!(0, fs_1.existsSync)(path_1.default.join(rootPath, name.split("/").splice(-1, 1).join("/")))) {
44
- await promises_1.default.mkdir(path_1.default.join(rootPath, name.replace('/' + file_helper_1.fh.filenameFromPath(name), '')), { recursive: true });
45
- }
39
+ const outDir = path_1.default.join(rootPath, (0, file_helper_1.getFilePathWithoutName)(name));
40
+ if (!(0, fs_1.existsSync)(outDir)) {
41
+ await promises_1.default.mkdir(outDir, { recursive: true });
46
42
  }
47
43
  const content = await promises_1.default.readFile(stubPath, 'utf8');
48
44
  const names = (0, file_helper_1.resolveNames)(file_helper_1.fh.filenameFromPath(name), 'Service');
@@ -55,8 +51,8 @@ async function createService(name, options = { force: false, resource: false, pr
55
51
  .replaceAll('{Model}', capitalize(options.model))
56
52
  .replaceAll('{model}', lower(options.model));
57
53
  }
58
- await promises_1.default.writeFile(path_1.default.join(rootPath, (0, file_helper_1.getFilePathWithoutName)(name) + "/" + (0, lodash_1.kebabCase)(className) + '.service.ts'), replacedContent, 'utf8');
59
- console.log("Service created succssessfully!");
54
+ await promises_1.default.writeFile(path_1.default.join(outDir, (0, lodash_1.kebabCase)(className) + '.service.ts'), replacedContent, 'utf8');
55
+ console.log("Service created successfully!");
60
56
  }
61
57
  async function createRestService(name, model) {
62
58
  let stubPath = path_helper_1.pathHelper.stubPath('services/rest-service.stub');
package/package.json CHANGED
@@ -1,61 +1,62 @@
1
- {
2
- "name": "@avleon/cli",
3
- "version": "0.0.45",
4
- "description": "CLI for scaffolding and running Avleon applications",
5
- "author": "Tareq Hossain",
6
- "license": "ISC",
7
- "main": "dist/index.js",
8
- "types": "dist/index.d.ts",
9
- "bin": {
10
- "avleon": "./dist/cli.js"
11
- },
12
- "files": [
13
- "dist",
14
- "README.md"
15
- ],
16
- "scripts": {
17
- "build": "rimraf dist && tsc",
18
- "clean": "rimraf dist",
19
- "prepublishOnly": "npm run build",
20
- "watch": "tsc-watch --onSuccess \"echo Build complete\""
21
- },
22
- "keywords": [
23
- "cli",
24
- "avleon",
25
- "backend",
26
- "typescript"
27
- ],
28
- "dependencies": {
29
- "@avleon/core": "*",
30
- "@swc/core": "^1.13.5",
31
- "chokidar": "^4.0.3",
32
- "chalk": "^5.4.1",
33
- "cli-table3": "^0.6.5",
34
- "fs-extra": "^11.2.0",
35
- "inquirer": "^12.6.0",
36
- "lodash": "^4.17.21",
37
- "prettier": "^3.3.3",
38
- "rimraf": "^6.0.1"
39
- },
40
- "devDependencies": {
41
- "@swc/cli": "^0.7.8",
42
- "@types/fs-extra": "^11.0.4",
43
- "@types/lodash": "^4.17.16",
44
- "@types/node": "^22.9.3",
45
- "concurrently": "^9.2.1",
46
- "nodemon": "^3.1.7",
47
- "tsc-watch": "^6.2.1",
48
- "tsup": "^8.4.0",
49
- "typescript": "^5.7.2"
50
- },
51
- "peerDependencies": {
52
- "@avleon/core": ">=0.0.38"
53
- },
54
- "repository": {
55
- "type": "git",
56
- "url": "git+https://github.com/avleonjs/avleon-cli"
57
- },
58
- "publishConfig": {
59
- "access": "public"
60
- }
61
- }
1
+ {
2
+ "name": "@avleon/cli",
3
+ "version": "0.0.47",
4
+ "description": "CLI for scaffolding and running Avleon applications",
5
+ "author": "Tareq Hossain",
6
+ "license": "ISC",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "bin": {
10
+ "avleon": "./dist/cli.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "stubs",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "rimraf dist && tsc",
19
+ "clean": "rimraf dist",
20
+ "prepublishOnly": "npm run build",
21
+ "watch": "tsc-watch --onSuccess \"echo Build complete\""
22
+ },
23
+ "keywords": [
24
+ "cli",
25
+ "avleon",
26
+ "backend",
27
+ "typescript"
28
+ ],
29
+ "dependencies": {
30
+ "@avleon/core": "*",
31
+ "@swc/core": "^1.13.5",
32
+ "chokidar": "^4.0.3",
33
+ "chalk": "^5.4.1",
34
+ "cli-table3": "^0.6.5",
35
+ "fs-extra": "^11.2.0",
36
+ "inquirer": "^12.6.0",
37
+ "lodash": "^4.17.21",
38
+ "prettier": "^3.3.3",
39
+ "rimraf": "^6.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "@swc/cli": "^0.7.8",
43
+ "@types/fs-extra": "^11.0.4",
44
+ "@types/lodash": "^4.17.16",
45
+ "@types/node": "^22.9.3",
46
+ "concurrently": "^9.2.1",
47
+ "nodemon": "^3.1.7",
48
+ "tsc-watch": "^6.2.1",
49
+ "tsup": "^8.4.0",
50
+ "typescript": "^5.7.2"
51
+ },
52
+ "peerDependencies": {
53
+ "@avleon/core": ">=0.0.38"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "git+https://github.com/avleonjs/avleon-cli"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ }
62
+ }
package/readme.md CHANGED
@@ -1,66 +1,116 @@
1
-
2
- ### ⚠️ WARNING: NOT FOR PRODUCTION USE
3
-
4
- > **🚧 This project is in active development.**
5
- >
6
- > It is **not stable** and **not ready** for live environments.
7
- > Use **only for testing, experimentation, or internal evaluation**.
8
- >
9
- > ####❗ Risks of using this in production:
10
- >
11
- > - 🔄 Breaking changes may be introduced at any time
12
- > - 🧪 Features are experimental and may be unstable
13
- > - 🔐 Security has not been audited
14
- > - 💥 Potential for data loss or critical errors
15
- >
16
- > **Please do not deploy this in production environments.**
17
-
18
- # Avleon CLI - v0.0.37
19
-
20
- ### Create Application
21
- ```sh
22
- npx @avleon/cli new my_api
23
- ```
24
- ### Create Controller
25
- ```sh
26
- npx avleon make:controller product
27
- # or shorthand
28
- npx avleon m:c product
29
- # for resource controller with -r flag
30
- npx avleon m:c product -m product -r
31
- ```
32
-
33
- ### Create Model/Entity
34
- ```sh
35
- # basic model
36
- npx avleon make:model product
37
- # typeorm model
38
- npx avleon m:m product --orm
39
- ```
40
-
41
- ### Create Service
42
- ```sh
43
- # basic service
44
- npx avleon make:service product
45
- # resource service with built-in collection
46
- npx avleon make:service product -m product -r
47
- # resource service with typeorm collection
48
- npx avleon make:service product -m product -r --orm
49
- ```
50
-
51
- ### Create Middleware
52
- ```sh
53
- npx avleon make:config product
54
- ```
55
-
56
- ### Create Config
57
- ```sh
58
- npx avleon make:config product
59
- ```
60
-
61
- ### Create authoriztion (Experimental)
62
- ```sh
63
- npx avleon make:auth jwt
64
- ```
65
-
66
-
1
+ ### ⚠️ WARNING: NOT FOR PRODUCTION USE
2
+
3
+ > **🚧 This project is in active development.**
4
+ >
5
+ > It is **not stable** and **not ready** for live environments.
6
+ > Use **only for testing, experimentation, or internal evaluation**.
7
+ >
8
+ > #### ❗ Risks of using this in production:
9
+ >
10
+ > - 🔄 Breaking changes may be introduced at any time
11
+ > - 🧪 Features are experimental and may be unstable
12
+ > - 🔐 Security has not been audited
13
+ > - 💥 Potential for data loss or critical errors
14
+ >
15
+ > **Please do not deploy this in production environments.**
16
+
17
+ # Avleon CLI
18
+
19
+ Scaffold and run [Avleon](https://github.com/avleonjs/avleon-core) applications.
20
+
21
+ ## Create an Application
22
+
23
+ ```sh
24
+ npx @avleon/cli new my_api
25
+ ```
26
+
27
+ You are prompted for a package manager and for optional features (OpenAPI,
28
+ CORS, TypeORM, Knex, Scalar UI). The generated project uses `@avleon/core`'s
29
+ `Avleon.createApplication()` and is ready to run:
30
+
31
+ ```sh
32
+ cd my_api
33
+ npm run start:dev
34
+ ```
35
+
36
+ ## Commands
37
+
38
+ | Command | Alias | Options | Description |
39
+ |---------|-------|---------|-------------|
40
+ | `new` | | `--minimal`, `-m` | Create a new application |
41
+ | `serve` | | | Start the dev server with watch + rebuild |
42
+ | `build` | | | Compile with `tsconfig.build.json` |
43
+ | `make:model` | `m:m` | `-f`, `--orm` | Create a model / TypeORM entity |
44
+ | `make:controller` | `m:c` | `-f`, `-r`, `-m <model>`, `-rt <dir>`, `--orm`, `--base-path` | Create a controller |
45
+ | `make:service` | `m:s` | `-f`, `-r`, `-m <model>`, `-rt <dir>`, `--orm` | Create a service |
46
+ | `make:config` | `m:cnf` | `-f` | Create a config class |
47
+ | `make:middleware` | `m:mlwr` | `-f` | Create a middleware |
48
+
49
+ `-f` / `--force` overwrites an existing file. `-r` / `--resource` generates a
50
+ full REST skeleton.
51
+
52
+ ### Controller
53
+
54
+ ```sh
55
+ npx avleon make:controller product
56
+ # shorthand
57
+ npx avleon m:c product
58
+
59
+ # REST controller — also generates the matching service
60
+ npx avleon m:c product -m product -r
61
+
62
+ # REST controller backed by a TypeORM repository
63
+ npx avleon m:c product -m product -r --orm
64
+ ```
65
+
66
+ With `-m`, the generated controller delegates to `src/services/<model>.service.ts`,
67
+ which is created alongside it.
68
+
69
+ ### Model / Entity
70
+
71
+ ```sh
72
+ # plain class
73
+ npx avleon make:model product
74
+
75
+ # TypeORM entity
76
+ npx avleon m:m product --orm
77
+ ```
78
+
79
+ ### Service
80
+
81
+ ```sh
82
+ # basic service
83
+ npx avleon make:service product
84
+
85
+ # resource service backed by an in-memory Collection
86
+ npx avleon make:service product -m product -r
87
+
88
+ # resource service backed by a TypeORM repository
89
+ npx avleon make:service product -m product -r --orm
90
+ ```
91
+
92
+ ### Middleware
93
+
94
+ ```sh
95
+ npx avleon make:middleware auth
96
+ ```
97
+
98
+ Generates a class extending `AvleonMiddleware` with an `invoke(req, res)` method.
99
+
100
+ ### Config
101
+
102
+ ```sh
103
+ npx avleon make:config jwt
104
+ ```
105
+
106
+ Generates a class extending `AvleonConfig`, registered via `@AppConfig`.
107
+
108
+ ## Development Server
109
+
110
+ ```sh
111
+ npx avleon serve
112
+ ```
113
+
114
+ Compiles `src/` with SWC into `dist/`, starts the entry point, and restarts on
115
+ change. The entry file is taken from `avleon.entry` in `package.json`, falling
116
+ back to `main`.