@mantiq/cli 0.1.6 → 0.2.0-rc.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantiq/cli",
3
- "version": "0.1.6",
3
+ "version": "0.2.0-rc.2",
4
4
  "description": "Command runner, code generators, dev server",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,14 +43,16 @@
43
43
  "LICENSE"
44
44
  ],
45
45
  "scripts": {
46
- "build": "bun build ./src/index.ts --outdir ./dist --target bun",
46
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun --packages=external",
47
47
  "test": "bun test",
48
48
  "typecheck": "tsc --noEmit",
49
49
  "clean": "rm -rf dist"
50
50
  },
51
51
  "devDependencies": {
52
52
  "bun-types": "latest",
53
- "typescript": "^5.7.0"
53
+ "typescript": "^5.7.0",
54
+ "@mantiq/core": "workspace:*",
55
+ "@mantiq/database": "workspace:*"
54
56
  },
55
57
  "peerDependencies": {
56
58
  "@mantiq/core": "^0.1.0",
@@ -0,0 +1,27 @@
1
+ import type { Command } from './Command.ts'
2
+
3
+ /**
4
+ * Global command registry. Service providers push their commands here
5
+ * during boot(), and the CLI Kernel collects them at run time.
6
+ *
7
+ * This decouples command registration from the Kernel — each package
8
+ * registers its own commands without the user needing to import them.
9
+ */
10
+
11
+ const _commands: Command[] = []
12
+
13
+ export function registerCommand(command: Command): void {
14
+ _commands.push(command)
15
+ }
16
+
17
+ export function registerCommands(commands: Command[]): void {
18
+ for (const cmd of commands) _commands.push(cmd)
19
+ }
20
+
21
+ export function getRegisteredCommands(): Command[] {
22
+ return [..._commands]
23
+ }
24
+
25
+ export function clearRegisteredCommands(): void {
26
+ _commands.length = 0
27
+ }
package/src/Kernel.ts CHANGED
@@ -1,6 +1,42 @@
1
1
  import type { Command } from './Command.ts'
2
2
  import { parse, type ParsedArgs } from './Parser.ts'
3
3
  import { IO } from './IO.ts'
4
+ import { getRegisteredCommands } from './CommandRegistry.ts'
5
+
6
+ // Built-in commands — utility
7
+ import { AboutCommand } from './commands/AboutCommand.ts'
8
+ import { ServeCommand } from './commands/ServeCommand.ts'
9
+ import { RouteListCommand } from './commands/RouteListCommand.ts'
10
+ import { TinkerCommand } from './commands/TinkerCommand.ts'
11
+
12
+ // Built-in commands — database
13
+ import { MigrateCommand } from './commands/MigrateCommand.ts'
14
+ import { MigrateRollbackCommand } from './commands/MigrateRollbackCommand.ts'
15
+ import { MigrateResetCommand } from './commands/MigrateResetCommand.ts'
16
+ import { MigrateFreshCommand } from './commands/MigrateFreshCommand.ts'
17
+ import { MigrateStatusCommand } from './commands/MigrateStatusCommand.ts'
18
+ import { SeedCommand } from './commands/SeedCommand.ts'
19
+
20
+ // Built-in commands — generators
21
+ import { MakeCommandCommand } from './commands/MakeCommandCommand.ts'
22
+ import { MakeControllerCommand } from './commands/MakeControllerCommand.ts'
23
+ import { MakeEventCommand } from './commands/MakeEventCommand.ts'
24
+ import { MakeExceptionCommand } from './commands/MakeExceptionCommand.ts'
25
+ import { MakeFactoryCommand } from './commands/MakeFactoryCommand.ts'
26
+ import { MakeJobCommand } from './commands/MakeJobCommand.ts'
27
+ import { MakeListenerCommand } from './commands/MakeListenerCommand.ts'
28
+ import { MakeMailCommand } from './commands/MakeMailCommand.ts'
29
+ import { MakeMiddlewareCommand } from './commands/MakeMiddlewareCommand.ts'
30
+ import { MakeMigrationCommand } from './commands/MakeMigrationCommand.ts'
31
+ import { MakeModelCommand } from './commands/MakeModelCommand.ts'
32
+ import { MakeNotificationCommand } from './commands/MakeNotificationCommand.ts'
33
+ import { MakeObserverCommand } from './commands/MakeObserverCommand.ts'
34
+ import { MakePolicyCommand } from './commands/MakePolicyCommand.ts'
35
+ import { MakeProviderCommand } from './commands/MakeProviderCommand.ts'
36
+ import { MakeRequestCommand } from './commands/MakeRequestCommand.ts'
37
+ import { MakeRuleCommand } from './commands/MakeRuleCommand.ts'
38
+ import { MakeSeederCommand } from './commands/MakeSeederCommand.ts'
39
+ import { MakeTestCommand } from './commands/MakeTestCommand.ts'
4
40
 
5
41
  /**
6
42
  * The CLI Kernel — registers commands and dispatches to the right one.
@@ -9,6 +45,7 @@ export class Kernel {
9
45
  private commands = new Map<string, Command>()
10
46
  private io = new IO()
11
47
  private discovered = false
48
+ private builtinsRegistered = false
12
49
 
13
50
  constructor(public readonly app: any = null) {}
14
51
 
@@ -22,6 +59,66 @@ export class Kernel {
22
59
  return this
23
60
  }
24
61
 
62
+ /**
63
+ * Register all built-in framework commands (utility, database, generators).
64
+ * Called once on the first run() invocation.
65
+ */
66
+ private registerBuiltins(): void {
67
+ this.registerAll([
68
+ // Utility
69
+ new AboutCommand(),
70
+ new ServeCommand(),
71
+ new RouteListCommand(),
72
+ new TinkerCommand(),
73
+
74
+ // Database
75
+ new MigrateCommand(),
76
+ new MigrateRollbackCommand(),
77
+ new MigrateResetCommand(),
78
+ new MigrateFreshCommand(),
79
+ new MigrateStatusCommand(),
80
+ new SeedCommand(),
81
+
82
+ // Generators
83
+ new MakeCommandCommand(),
84
+ new MakeControllerCommand(),
85
+ new MakeEventCommand(),
86
+ new MakeExceptionCommand(),
87
+ new MakeFactoryCommand(),
88
+ new MakeJobCommand(),
89
+ new MakeListenerCommand(),
90
+ new MakeMailCommand(),
91
+ new MakeMiddlewareCommand(),
92
+ new MakeMigrationCommand(),
93
+ new MakeModelCommand(),
94
+ new MakeNotificationCommand(),
95
+ new MakeObserverCommand(),
96
+ new MakePolicyCommand(),
97
+ new MakeProviderCommand(),
98
+ new MakeRequestCommand(),
99
+ new MakeRuleCommand(),
100
+ new MakeSeederCommand(),
101
+ new MakeTestCommand(),
102
+ ])
103
+ }
104
+
105
+ /**
106
+ * Dynamically import and register commands from optional packages.
107
+ * Uses try/catch so the CLI works fine when a package isn't installed.
108
+ *
109
+ * Note: Queue commands (queue:work, queue:retry, etc.) require a QueueManager
110
+ * instance and are registered by QueueServiceProvider.boot() instead.
111
+ */
112
+ private async registerPackageCommands(): Promise<void> {
113
+ // Heartbeat commands (if @mantiq/heartbeat is installed)
114
+ try {
115
+ const { InstallCommand } = await import('@mantiq/heartbeat')
116
+ this.register(new InstallCommand())
117
+ } catch {
118
+ // @mantiq/heartbeat not installed — skip heartbeat commands
119
+ }
120
+ }
121
+
25
122
  /**
26
123
  * Auto-discover and register command files from app/Console/Commands/.
27
124
  * Each file must export a class that extends Command.
@@ -55,11 +152,24 @@ export class Kernel {
55
152
  // Directory doesn't exist — no commands to discover
56
153
  }
57
154
 
155
+ // Collect commands registered by service providers via CommandRegistry
156
+ for (const cmd of getRegisteredCommands()) {
157
+ if (!this.commands.has(cmd.name)) {
158
+ this.register(cmd)
159
+ }
160
+ }
161
+
58
162
  this.discovered = true
59
163
  return this
60
164
  }
61
165
 
62
166
  async run(argv: string[] = process.argv): Promise<number> {
167
+ if (!this.builtinsRegistered) {
168
+ this.registerBuiltins()
169
+ await this.registerPackageCommands()
170
+ this.builtinsRegistered = true
171
+ }
172
+
63
173
  await this.discover()
64
174
  const parsed = parse(argv)
65
175
 
@@ -0,0 +1,34 @@
1
+ import { GeneratorCommand } from './GeneratorCommand.ts'
2
+ import type { ParsedArgs } from '../Parser.ts'
3
+
4
+ export class MakeJobCommand extends GeneratorCommand {
5
+ override name = 'make:job'
6
+ override description = 'Create a new job class'
7
+ override usage = 'make:job <name>'
8
+
9
+ override directory() { return 'app/Jobs' }
10
+ override suffix() { return '' }
11
+
12
+ override stub(name: string): string {
13
+ return `import { Job } from '@mantiq/queue'
14
+
15
+ export class ${name} extends Job {
16
+ override tries = 3
17
+ override backoff = 10
18
+
19
+ constructor(public readonly data: Record<string, any> = {}) {
20
+ super()
21
+ }
22
+
23
+ override async handle(): Promise<void> {
24
+ // TODO: implement ${name} job logic
25
+ }
26
+
27
+ override async failed(error: Error): Promise<void> {
28
+ // TODO: handle failure (e.g., log, notify)
29
+ console.error(\`${name} failed: \${error.message}\`)
30
+ }
31
+ }
32
+ `
33
+ }
34
+ }
@@ -0,0 +1,34 @@
1
+ import { GeneratorCommand } from './GeneratorCommand.ts'
2
+ import type { ParsedArgs } from '../Parser.ts'
3
+
4
+ export class MakeMailCommand extends GeneratorCommand {
5
+ override name = 'make:mail'
6
+ override description = 'Create a new mailable class'
7
+ override usage = 'make:mail <name>'
8
+
9
+ override directory() { return 'app/Mail' }
10
+ override suffix() { return '' }
11
+
12
+ override stub(name: string): string {
13
+ return `import { Mailable } from '@mantiq/mail'
14
+
15
+ export class ${name} extends Mailable {
16
+ constructor(private readonly data: Record<string, any> = {}) {
17
+ super()
18
+ }
19
+
20
+ override build(): void {
21
+ this.setSubject('${this.toSubject(name)}')
22
+ this.html(\`
23
+ <h1>${this.toSubject(name)}</h1>
24
+ <p>This is the ${name} mailable.</p>
25
+ \`)
26
+ }
27
+ }
28
+ `
29
+ }
30
+
31
+ private toSubject(name: string): string {
32
+ return name.replace(/([A-Z])/g, ' $1').trim()
33
+ }
34
+ }
@@ -0,0 +1,49 @@
1
+ import { GeneratorCommand } from './GeneratorCommand.ts'
2
+ import type { ParsedArgs } from '../Parser.ts'
3
+
4
+ export class MakeNotificationCommand extends GeneratorCommand {
5
+ override name = 'make:notification'
6
+ override description = 'Create a new notification class'
7
+ override usage = 'make:notification <name>'
8
+
9
+ override directory() { return 'app/Notifications' }
10
+ override suffix() { return '' }
11
+
12
+ override stub(name: string): string {
13
+ return `import { Notification } from '@mantiq/notify'
14
+ import type { Notifiable } from '@mantiq/notify'
15
+
16
+ export class ${name} extends Notification {
17
+ constructor(private readonly data: Record<string, any> = {}) {
18
+ super()
19
+ }
20
+
21
+ override via(notifiable: Notifiable): string[] {
22
+ return ['mail', 'database']
23
+ }
24
+
25
+ override toMail(notifiable: Notifiable): any {
26
+ return {
27
+ subject: '${this.toSubject(name)}',
28
+ html: '<p>${this.toSubject(name)}</p>',
29
+ }
30
+ }
31
+
32
+ override toDatabase(notifiable: Notifiable): Record<string, any> {
33
+ return {
34
+ type: '${this.toSnake(name)}',
35
+ ...this.data,
36
+ }
37
+ }
38
+ }
39
+ `
40
+ }
41
+
42
+ private toSubject(name: string): string {
43
+ return name.replace(/([A-Z])/g, ' $1').trim()
44
+ }
45
+
46
+ private toSnake(name: string): string {
47
+ return name.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '')
48
+ }
49
+ }
@@ -0,0 +1,47 @@
1
+ import { GeneratorCommand } from './GeneratorCommand.ts'
2
+ import type { ParsedArgs } from '../Parser.ts'
3
+
4
+ export class MakePolicyCommand extends GeneratorCommand {
5
+ override name = 'make:policy'
6
+ override description = 'Create a new policy class'
7
+ override usage = 'make:policy <name> [--model=ModelName]'
8
+
9
+ override directory() { return 'app/Policies' }
10
+ override suffix() { return 'Policy' }
11
+
12
+ override stub(name: string, args: ParsedArgs): string {
13
+ const modelName = (args.flags['model'] as string) ?? name
14
+ const modelImport = `import { ${modelName} } from '../Models/${modelName}.ts'`
15
+
16
+ return `import { Policy } from '@mantiq/auth'
17
+ ${modelImport}
18
+
19
+ export class ${name}Policy extends Policy {
20
+ /**
21
+ * Called before any other policy method.
22
+ * Return true to allow, false to deny, null to continue to the specific check.
23
+ */
24
+ // before(user: any, ability: string): boolean | null {
25
+ // if (user.role === 'admin') return true
26
+ // return null
27
+ // }
28
+
29
+ view(user: any, ${modelName.toLowerCase()}: ${modelName}): boolean {
30
+ return true
31
+ }
32
+
33
+ create(user: any): boolean {
34
+ return true
35
+ }
36
+
37
+ update(user: any, ${modelName.toLowerCase()}: ${modelName}): boolean {
38
+ return user.getAuthIdentifier() === ${modelName.toLowerCase()}.getAttribute('user_id')
39
+ }
40
+
41
+ delete(user: any, ${modelName.toLowerCase()}: ${modelName}): boolean {
42
+ return user.getAuthIdentifier() === ${modelName.toLowerCase()}.getAttribute('user_id')
43
+ }
44
+ }
45
+ `
46
+ }
47
+ }
@@ -74,6 +74,20 @@ export class TinkerCommand extends Command {
74
74
  }
75
75
  }
76
76
 
77
+ // ── Auto-import factories ─────────────────────────────────────────────
78
+ const factoriesDir = `${process.cwd()}/database/factories`
79
+ if (existsSync(factoriesDir)) {
80
+ const files = readdirSync(factoriesDir).filter((f) => f.endsWith('.ts'))
81
+ for (const file of files) {
82
+ try {
83
+ const mod = await import(`${factoriesDir}/${file}`)
84
+ const name = file.replace(/\.ts$/, '')
85
+ const cls = mod[name] ?? mod.default ?? Object.values(mod)[0]
86
+ if (cls) context[name] = cls
87
+ } catch {}
88
+ }
89
+ }
90
+
77
91
  // ── Assign to globalThis ─────────────────────────────────────────────
78
92
  for (const [key, value] of Object.entries(context)) {
79
93
  ;(globalThis as any)[key] = value
package/src/index.ts CHANGED
@@ -7,6 +7,9 @@ export { IO } from './IO.ts'
7
7
  export { parse } from './Parser.ts'
8
8
  export type { ParsedArgs } from './Parser.ts'
9
9
 
10
+ // Command Registry (service providers register commands here)
11
+ export { registerCommand, registerCommands, getRegisteredCommands, clearRegisteredCommands } from './CommandRegistry.ts'
12
+
10
13
  // Base
11
14
  export { GeneratorCommand } from './commands/GeneratorCommand.ts'
12
15
 
@@ -34,6 +37,10 @@ export { MakeRequestCommand } from './commands/MakeRequestCommand.ts'
34
37
  export { MakeRuleCommand } from './commands/MakeRuleCommand.ts'
35
38
  export { MakeSeederCommand } from './commands/MakeSeederCommand.ts'
36
39
  export { MakeTestCommand } from './commands/MakeTestCommand.ts'
40
+ export { MakeMailCommand } from './commands/MakeMailCommand.ts'
41
+ export { MakeNotificationCommand } from './commands/MakeNotificationCommand.ts'
42
+ export { MakeJobCommand } from './commands/MakeJobCommand.ts'
43
+ export { MakePolicyCommand } from './commands/MakePolicyCommand.ts'
37
44
 
38
45
  // Utility commands
39
46
  export { AboutCommand } from './commands/AboutCommand.ts'