@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 +5 -3
- package/src/CommandRegistry.ts +27 -0
- package/src/Kernel.ts +110 -0
- package/src/commands/MakeJobCommand.ts +34 -0
- package/src/commands/MakeMailCommand.ts +34 -0
- package/src/commands/MakeNotificationCommand.ts +49 -0
- package/src/commands/MakePolicyCommand.ts +47 -0
- package/src/commands/TinkerCommand.ts +14 -0
- package/src/index.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantiq/cli",
|
|
3
|
-
"version": "0.
|
|
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'
|