@rpal/cli 1.0.0 → 1.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.
- package/dist/bin/create.js +7 -0
- package/dist/commands/create.js +246 -0
- package/dist/index.js +266 -223
- package/dist/templates.js +26 -0
- package/package.json +8 -8
- package/LICENSE +0 -21
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import gradient from 'gradient-string';
|
|
2
|
+
import { downloadTemplate } from 'giget';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, cpSync, rmSync, readdirSync } from 'fs';
|
|
5
|
+
import { join, isAbsolute } from 'path';
|
|
6
|
+
import { templates, getTemplateByAlias } from '../templates.js';
|
|
7
|
+
const VERSION = '1.0.0';
|
|
8
|
+
export class CreateNewApp {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.projectName = '';
|
|
11
|
+
this.projectDir = '';
|
|
12
|
+
this.selectedTemplate = '';
|
|
13
|
+
this.packageManager = 'npm';
|
|
14
|
+
this.gitInit = false;
|
|
15
|
+
this.verbose = false;
|
|
16
|
+
}
|
|
17
|
+
async run() {
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
|
20
|
+
const arg = args[i];
|
|
21
|
+
if (arg === '--template' || arg === '-t') {
|
|
22
|
+
this.selectedTemplate = args[++i];
|
|
23
|
+
}
|
|
24
|
+
else if (arg === '--git' || arg === '-g') {
|
|
25
|
+
this.gitInit = true;
|
|
26
|
+
}
|
|
27
|
+
else if (arg === '--verbose' || arg === '-v') {
|
|
28
|
+
this.verbose = true;
|
|
29
|
+
}
|
|
30
|
+
else if (arg === '--help' || arg === '-h') {
|
|
31
|
+
this.showHelp();
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
else if (!arg.startsWith('-')) {
|
|
35
|
+
this.projectName = arg;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const whichPmRuns = await import('which-pm-runs');
|
|
39
|
+
const pm = whichPmRuns.default();
|
|
40
|
+
this.packageManager = pm?.name || 'npm';
|
|
41
|
+
this.printBanner();
|
|
42
|
+
if (!this.projectName) {
|
|
43
|
+
this.projectName = await this.prompt('project name', 'my-pal-app');
|
|
44
|
+
}
|
|
45
|
+
this.projectDir = isAbsolute(this.projectName)
|
|
46
|
+
? this.projectName
|
|
47
|
+
: join(process.cwd(), this.projectName);
|
|
48
|
+
if (!this.selectedTemplate) {
|
|
49
|
+
this.selectedTemplate = await this.promptChoice('Select a starter kit', templates.map((t) => ({ name: t.name, value: t.alias })));
|
|
50
|
+
}
|
|
51
|
+
const template = getTemplateByAlias(this.selectedTemplate);
|
|
52
|
+
if (!template) {
|
|
53
|
+
throw new Error(`Unknown template: ${this.selectedTemplate}`);
|
|
54
|
+
}
|
|
55
|
+
await this.downloadTemplate(template.source);
|
|
56
|
+
await this.prepareProject();
|
|
57
|
+
await this.installDependencies();
|
|
58
|
+
if (this.gitInit) {
|
|
59
|
+
await this.initGit();
|
|
60
|
+
}
|
|
61
|
+
this.printSuccess();
|
|
62
|
+
}
|
|
63
|
+
showHelp() {
|
|
64
|
+
console.log(`
|
|
65
|
+
Usage: create-pal [project-name] [options]
|
|
66
|
+
|
|
67
|
+
Options:
|
|
68
|
+
-t, --template <name> Starter kit template (api, admin, client)
|
|
69
|
+
-g, --git Initialize git repository
|
|
70
|
+
-v, --verbose Enable verbose mode
|
|
71
|
+
-h, --help Show this help message
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
create-pal my-project
|
|
75
|
+
create-pal my-project --template api
|
|
76
|
+
create-pal my-project --git
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
printBanner() {
|
|
80
|
+
const title = `
|
|
81
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
82
|
+
║ ║
|
|
83
|
+
║ ███████╗██╗ ██╗███████╗████████╗███████╗███╗ ███╗ ║
|
|
84
|
+
║ ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔════╝████╗ ████║ ║
|
|
85
|
+
║ ███████╗ ╚████╔╝ ███████╗ ██║ █████╗ ██╔████╔██║ ║
|
|
86
|
+
║ ╚════██║ ╚██╔╝ ╚════██║ ██║ ██╔══╝ ██║╚██╔╝██║ ║
|
|
87
|
+
║ ███████║ ██║ ███████║ ██║ ███████╗██║ ╚═╝ ██║ ║
|
|
88
|
+
║ ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║
|
|
89
|
+
║ ║
|
|
90
|
+
║ ${VERSION} - The batteries-included full-stack framework ║
|
|
91
|
+
║ ║
|
|
92
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
93
|
+
`.trim();
|
|
94
|
+
console.log(gradient.pastel.multiline(title));
|
|
95
|
+
console.log();
|
|
96
|
+
}
|
|
97
|
+
async prompt(question, defaultValue) {
|
|
98
|
+
const readline = await import('readline');
|
|
99
|
+
const rl = readline.createInterface({
|
|
100
|
+
input: process.stdin,
|
|
101
|
+
output: process.stdout,
|
|
102
|
+
});
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
rl.question(`${question} (${defaultValue}): `, (answer) => {
|
|
105
|
+
rl.close();
|
|
106
|
+
resolve(answer.trim() || defaultValue);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async promptChoice(question, choices) {
|
|
111
|
+
console.log(`\n${question}:`);
|
|
112
|
+
choices.forEach((choice, index) => {
|
|
113
|
+
console.log(` ${index + 1}. ${choice.name}`);
|
|
114
|
+
});
|
|
115
|
+
console.log();
|
|
116
|
+
const answer = await this.prompt('Enter choice number', '1');
|
|
117
|
+
const index = parseInt(answer, 10) - 1;
|
|
118
|
+
if (index >= 0 && index < choices.length) {
|
|
119
|
+
return choices[index].value;
|
|
120
|
+
}
|
|
121
|
+
return choices[0].value;
|
|
122
|
+
}
|
|
123
|
+
async downloadTemplate(source) {
|
|
124
|
+
console.log(`\n📦 Downloading template from: ${source}`);
|
|
125
|
+
if (existsSync(this.projectDir)) {
|
|
126
|
+
const files = readdirSync(this.projectDir);
|
|
127
|
+
if (files.length > 0) {
|
|
128
|
+
rmSync(this.projectDir, { recursive: true, force: true });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
await downloadTemplate(source, {
|
|
132
|
+
dir: this.projectDir,
|
|
133
|
+
registry: false,
|
|
134
|
+
});
|
|
135
|
+
console.log('✅ Template downloaded successfully');
|
|
136
|
+
}
|
|
137
|
+
async prepareProject() {
|
|
138
|
+
console.log('\n🔧 Preparing project...');
|
|
139
|
+
const pkgJsonPath = join(this.projectDir, 'package.json');
|
|
140
|
+
if (existsSync(pkgJsonPath)) {
|
|
141
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
142
|
+
pkgJson.name = this.projectName.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
|
|
143
|
+
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
144
|
+
}
|
|
145
|
+
const envExample = join(this.projectDir, '.env.example');
|
|
146
|
+
const envFile = join(this.projectDir, '.env');
|
|
147
|
+
if (existsSync(envExample) && !existsSync(envFile)) {
|
|
148
|
+
cpSync(envExample, envFile);
|
|
149
|
+
}
|
|
150
|
+
const gitignore = join(this.projectDir, '.gitignore');
|
|
151
|
+
const gitignoreContent = `
|
|
152
|
+
node_modules/
|
|
153
|
+
dist/
|
|
154
|
+
build/
|
|
155
|
+
.turbo/
|
|
156
|
+
.env
|
|
157
|
+
*.log
|
|
158
|
+
coverage/
|
|
159
|
+
.vscode/
|
|
160
|
+
.idea/
|
|
161
|
+
`;
|
|
162
|
+
if (existsSync(gitignore)) {
|
|
163
|
+
const existing = readFileSync(gitignore, 'utf-8');
|
|
164
|
+
writeFileSync(gitignore, existing + gitignoreContent);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
writeFileSync(gitignore, gitignoreContent.trim() + '\n');
|
|
168
|
+
}
|
|
169
|
+
const lockFiles = [
|
|
170
|
+
join(this.projectDir, 'package-lock.json'),
|
|
171
|
+
join(this.projectDir, 'pnpm-lock.yaml'),
|
|
172
|
+
join(this.projectDir, 'yarn.lock'),
|
|
173
|
+
];
|
|
174
|
+
lockFiles.forEach((file) => {
|
|
175
|
+
if (existsSync(file)) {
|
|
176
|
+
rmSync(file);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
console.log('✅ Project prepared successfully');
|
|
180
|
+
}
|
|
181
|
+
async installDependencies() {
|
|
182
|
+
if (!existsSync(join(this.projectDir, 'package.json'))) {
|
|
183
|
+
console.log('⚠️ No package.json found, skipping install');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log(`\n📦 Installing dependencies using ${this.packageManager}...`);
|
|
187
|
+
const spinner = this.spinner('Installing');
|
|
188
|
+
try {
|
|
189
|
+
await execa(this.packageManager, ['install'], {
|
|
190
|
+
cwd: this.projectDir,
|
|
191
|
+
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
192
|
+
});
|
|
193
|
+
spinner.stop();
|
|
194
|
+
console.log('✅ Dependencies installed successfully');
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
spinner.stop();
|
|
198
|
+
throw new Error(`Failed to install dependencies: ${error.message}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async initGit() {
|
|
202
|
+
console.log('\n📂 Initializing git repository...');
|
|
203
|
+
try {
|
|
204
|
+
await execa('git', ['init'], {
|
|
205
|
+
cwd: this.projectDir,
|
|
206
|
+
stdio: this.verbose ? 'inherit' : 'pipe',
|
|
207
|
+
});
|
|
208
|
+
console.log('✅ Git repository initialized');
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
console.log('⚠️ Failed to initialize git (git may not be installed)');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
printSuccess() {
|
|
215
|
+
const message = `
|
|
216
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
217
|
+
║ ║
|
|
218
|
+
║ ✅ Your Pal project has been created successfully! ║
|
|
219
|
+
║ ║
|
|
220
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
221
|
+
|
|
222
|
+
Next steps:
|
|
223
|
+
cd ${this.projectName}
|
|
224
|
+
npm run dev
|
|
225
|
+
|
|
226
|
+
Documentation: https://github.com/paljs/paljs
|
|
227
|
+
`.trim();
|
|
228
|
+
console.log(gradient.pastel.multiline(message));
|
|
229
|
+
}
|
|
230
|
+
spinner(message) {
|
|
231
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
232
|
+
let i = 0;
|
|
233
|
+
const interval = setInterval(() => {
|
|
234
|
+
process.stdout.write(`\r${frames[i++ % frames.length]} ${message}`);
|
|
235
|
+
}, 80);
|
|
236
|
+
return {
|
|
237
|
+
stop: () => {
|
|
238
|
+
clearInterval(interval);
|
|
239
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
240
|
+
},
|
|
241
|
+
update: (msg) => {
|
|
242
|
+
process.stdout.write(`\r${msg}`);
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
const VERSION = '1.0.0';
|
|
5
|
-
|
|
5
|
+
const banner = `
|
|
6
6
|
╔═══════════════════════════════════════════╗
|
|
7
7
|
║ ║
|
|
8
8
|
║ ██████╗ ███████╗██╗ ██╗███████╗ ║
|
|
@@ -15,7 +15,7 @@ console.log(`
|
|
|
15
15
|
║ CLI Tool v${VERSION} ║
|
|
16
16
|
║ ║
|
|
17
17
|
╚═══════════════════════════════════════════╝
|
|
18
|
-
|
|
18
|
+
`;
|
|
19
19
|
const args = process.argv.slice(2);
|
|
20
20
|
const command = args[0];
|
|
21
21
|
if (!command || command === 'help') {
|
|
@@ -26,65 +26,99 @@ switch (command) {
|
|
|
26
26
|
case 'make:model':
|
|
27
27
|
makeModel(args.slice(1));
|
|
28
28
|
break;
|
|
29
|
-
case 'make:repository':
|
|
30
|
-
makeRepository(args.slice(1));
|
|
31
|
-
break;
|
|
32
29
|
case 'make:service':
|
|
33
30
|
makeService(args.slice(1));
|
|
34
31
|
break;
|
|
35
|
-
case 'make:migration':
|
|
36
|
-
makeMigration(args.slice(1));
|
|
37
|
-
break;
|
|
38
32
|
case 'make:controller':
|
|
39
33
|
makeController(args.slice(1));
|
|
40
34
|
break;
|
|
35
|
+
case 'make:middleware':
|
|
36
|
+
makeMiddleware(args.slice(1));
|
|
37
|
+
break;
|
|
38
|
+
case 'make:migration':
|
|
39
|
+
makeMigration(args.slice(1));
|
|
40
|
+
break;
|
|
41
41
|
case 'make:factory':
|
|
42
42
|
makeFactory(args.slice(1));
|
|
43
43
|
break;
|
|
44
|
-
case '
|
|
45
|
-
|
|
44
|
+
case 'make:exception':
|
|
45
|
+
makeException(args.slice(1));
|
|
46
|
+
break;
|
|
47
|
+
case 'make:validator':
|
|
48
|
+
makeValidator(args.slice(1));
|
|
49
|
+
break;
|
|
50
|
+
case 'db:migrate':
|
|
51
|
+
dbMigrate();
|
|
46
52
|
break;
|
|
47
|
-
case 'db:
|
|
48
|
-
|
|
53
|
+
case 'db:rollback':
|
|
54
|
+
dbRollback();
|
|
49
55
|
break;
|
|
50
56
|
case 'db:seed':
|
|
51
57
|
dbSeed();
|
|
52
58
|
break;
|
|
59
|
+
case 'db:fresh':
|
|
60
|
+
dbFresh();
|
|
61
|
+
break;
|
|
62
|
+
case 'route:list':
|
|
63
|
+
routeList();
|
|
64
|
+
break;
|
|
65
|
+
case 'key:generate':
|
|
66
|
+
keyGenerate();
|
|
67
|
+
break;
|
|
68
|
+
case 'list':
|
|
69
|
+
listRoutes();
|
|
70
|
+
break;
|
|
53
71
|
default:
|
|
54
72
|
console.log(`❌ Unknown command: ${command}`);
|
|
55
73
|
console.log(` Run 'pal help' for available commands`);
|
|
56
74
|
process.exit(1);
|
|
57
75
|
}
|
|
58
76
|
function showHelp() {
|
|
77
|
+
console.log(banner);
|
|
59
78
|
console.log(`
|
|
60
79
|
Usage: pal <command> [options]
|
|
61
80
|
|
|
81
|
+
Project Commands:
|
|
82
|
+
npm create pal@latest Create a new Pal project
|
|
83
|
+
Run: npm create pal@latest my-project
|
|
84
|
+
|
|
62
85
|
Make Commands:
|
|
63
|
-
make:model <name>
|
|
64
|
-
make:
|
|
65
|
-
make:
|
|
66
|
-
make:
|
|
67
|
-
make:migration <name>
|
|
68
|
-
make:factory <name>
|
|
86
|
+
make:model <name> Create a new model
|
|
87
|
+
make:service <name> Create a new service
|
|
88
|
+
make:controller <name> Create a new controller
|
|
89
|
+
make:middleware <name> Create a new middleware
|
|
90
|
+
make:migration <name> Create a new migration
|
|
91
|
+
make:factory <name> Create a model factory
|
|
92
|
+
make:exception <name> Create an exception class
|
|
93
|
+
make:validator <name> Create a validator
|
|
69
94
|
|
|
70
95
|
Database Commands:
|
|
71
|
-
db:
|
|
72
|
-
db:
|
|
96
|
+
db:migrate Run database migrations
|
|
97
|
+
db:rollback Rollback the last migration
|
|
98
|
+
db:seed Run database seeders
|
|
99
|
+
db:fresh Drop all tables and re-run migrations
|
|
73
100
|
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
Utility Commands:
|
|
102
|
+
route:list List all registered routes
|
|
103
|
+
key:generate Generate a new app key
|
|
104
|
+
list Alias for route:list
|
|
76
105
|
|
|
77
106
|
Options:
|
|
78
|
-
--resource
|
|
79
|
-
--api
|
|
80
|
-
--
|
|
107
|
+
--resource Generate CRUD methods
|
|
108
|
+
--api Generate API-ready controller
|
|
109
|
+
--http Create HTTP controller
|
|
110
|
+
--force Overwrite existing files
|
|
81
111
|
|
|
82
112
|
Examples:
|
|
113
|
+
npm create pal@latest my-project
|
|
83
114
|
pal make:model User
|
|
84
|
-
pal make:
|
|
115
|
+
pal make:service UserService
|
|
85
116
|
pal make:controller UserController --api
|
|
86
|
-
pal
|
|
87
|
-
|
|
117
|
+
pal make:middleware Auth
|
|
118
|
+
pal make:migration create_users_table
|
|
119
|
+
pal db:migrate
|
|
120
|
+
pal route:list
|
|
121
|
+
`);
|
|
88
122
|
}
|
|
89
123
|
function makeModel(args) {
|
|
90
124
|
const name = args[0];
|
|
@@ -95,61 +129,60 @@ function makeModel(args) {
|
|
|
95
129
|
}
|
|
96
130
|
const options = parseOptions(args);
|
|
97
131
|
const fileName = toKebabCase(name);
|
|
98
|
-
const dir = path.join(process.cwd(), '
|
|
99
|
-
ensureDir(dir);
|
|
100
|
-
const entityCode = generateEntity(name, options);
|
|
101
|
-
const repositoryCode = generateRepository(name, options);
|
|
102
|
-
writeFile(path.join(dir, 'index.ts'), entityCode);
|
|
103
|
-
writeFile(path.join(dir, 'repository.ts'), repositoryCode);
|
|
104
|
-
console.log(`✅ Created model: src/models/${fileName}/`);
|
|
105
|
-
console.log(` - index.ts`);
|
|
106
|
-
console.log(` - repository.ts`);
|
|
107
|
-
}
|
|
108
|
-
function makeRepository(args) {
|
|
109
|
-
const name = args[0];
|
|
110
|
-
if (!name) {
|
|
111
|
-
console.log('❌ Please provide a repository name');
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
const options = parseOptions(args);
|
|
115
|
-
const fileName = toKebabCase(name);
|
|
116
|
-
const dir = path.join(process.cwd(), 'src', 'repositories');
|
|
132
|
+
const dir = path.join(process.cwd(), 'app', 'models', fileName);
|
|
117
133
|
ensureDir(dir);
|
|
118
|
-
const
|
|
119
|
-
writeFile(path.join(dir,
|
|
120
|
-
console.log(`✅ Created
|
|
134
|
+
const modelCode = generateModel(name, options);
|
|
135
|
+
writeFile(path.join(dir, 'index.ts'), modelCode);
|
|
136
|
+
console.log(`✅ Created model: app/models/${fileName}/index.ts`);
|
|
121
137
|
}
|
|
122
138
|
function makeService(args) {
|
|
123
139
|
const name = args[0];
|
|
124
140
|
if (!name) {
|
|
125
141
|
console.log('❌ Please provide a service name');
|
|
142
|
+
console.log(' Example: pal make:service UserService');
|
|
126
143
|
process.exit(1);
|
|
127
144
|
}
|
|
128
145
|
const fileName = toKebabCase(name);
|
|
129
|
-
const dir = path.join(process.cwd(), '
|
|
146
|
+
const dir = path.join(process.cwd(), 'app', 'services');
|
|
130
147
|
ensureDir(dir);
|
|
131
148
|
const code = generateService(name);
|
|
132
149
|
writeFile(path.join(dir, `${fileName}.service.ts`), code);
|
|
133
|
-
console.log(`✅ Created service:
|
|
150
|
+
console.log(`✅ Created service: app/services/${fileName}.service.ts`);
|
|
134
151
|
}
|
|
135
152
|
function makeController(args) {
|
|
136
153
|
const name = args[0];
|
|
137
154
|
if (!name) {
|
|
138
155
|
console.log('❌ Please provide a controller name');
|
|
156
|
+
console.log(' Example: pal make:controller UserController');
|
|
139
157
|
process.exit(1);
|
|
140
158
|
}
|
|
141
159
|
const options = parseOptions(args);
|
|
142
160
|
const fileName = toKebabCase(name);
|
|
143
|
-
const dir = path.join(process.cwd(), '
|
|
161
|
+
const dir = path.join(process.cwd(), 'app', 'controllers', 'http');
|
|
144
162
|
ensureDir(dir);
|
|
145
163
|
const code = generateController(name, options);
|
|
146
164
|
writeFile(path.join(dir, `${fileName}.controller.ts`), code);
|
|
147
|
-
console.log(`✅ Created controller:
|
|
165
|
+
console.log(`✅ Created controller: app/controllers/http/${fileName}.controller.ts`);
|
|
166
|
+
}
|
|
167
|
+
function makeMiddleware(args) {
|
|
168
|
+
const name = args[0];
|
|
169
|
+
if (!name) {
|
|
170
|
+
console.log('❌ Please provide a middleware name');
|
|
171
|
+
console.log(' Example: pal make:middleware Auth');
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
const fileName = toKebabCase(name);
|
|
175
|
+
const dir = path.join(process.cwd(), 'app', 'middleware');
|
|
176
|
+
ensureDir(dir);
|
|
177
|
+
const code = generateMiddleware(name);
|
|
178
|
+
writeFile(path.join(dir, `${fileName}.ts`), code);
|
|
179
|
+
console.log(`✅ Created middleware: app/middleware/${fileName}.ts`);
|
|
148
180
|
}
|
|
149
181
|
function makeMigration(args) {
|
|
150
182
|
const name = args[0];
|
|
151
183
|
if (!name) {
|
|
152
184
|
console.log('❌ Please provide a migration name');
|
|
185
|
+
console.log(' Example: pal make:migration create_users_table');
|
|
153
186
|
process.exit(1);
|
|
154
187
|
}
|
|
155
188
|
const timestamp = Date.now();
|
|
@@ -164,6 +197,7 @@ function makeFactory(args) {
|
|
|
164
197
|
const name = args[0];
|
|
165
198
|
if (!name) {
|
|
166
199
|
console.log('❌ Please provide a factory name');
|
|
200
|
+
console.log(' Example: pal make:factory UserFactory');
|
|
167
201
|
process.exit(1);
|
|
168
202
|
}
|
|
169
203
|
const fileName = toKebabCase(name);
|
|
@@ -173,68 +207,65 @@ function makeFactory(args) {
|
|
|
173
207
|
writeFile(path.join(dir, `${fileName}.factory.ts`), code);
|
|
174
208
|
console.log(`✅ Created factory: database/factories/${fileName}.factory.ts`);
|
|
175
209
|
}
|
|
176
|
-
function
|
|
177
|
-
const name = args[0]
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
function makeException(args) {
|
|
211
|
+
const name = args[0];
|
|
212
|
+
if (!name) {
|
|
213
|
+
console.log('❌ Please provide an exception name');
|
|
214
|
+
console.log(' Example: pal make:exception NotFoundException');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
const fileName = toKebabCase(name);
|
|
218
|
+
const dir = path.join(process.cwd(), 'app', 'exceptions');
|
|
219
|
+
ensureDir(dir);
|
|
220
|
+
const code = generateException(name);
|
|
221
|
+
writeFile(path.join(dir, `${fileName}.ts`), code);
|
|
222
|
+
console.log(`✅ Created exception: app/exceptions/${fileName}.ts`);
|
|
223
|
+
}
|
|
224
|
+
function makeValidator(args) {
|
|
225
|
+
const name = args[0];
|
|
226
|
+
if (!name) {
|
|
227
|
+
console.log('❌ Please provide a validator name');
|
|
228
|
+
console.log(' Example: pal make:validator CreateUserValidator');
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
const fileName = toKebabCase(name);
|
|
232
|
+
const dir = path.join(process.cwd(), 'app', 'validators');
|
|
180
233
|
ensureDir(dir);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
scripts: {
|
|
193
|
-
dev: 'pal serve',
|
|
194
|
-
build: 'pal build',
|
|
195
|
-
test: 'pal test',
|
|
196
|
-
'db:push': 'pal db:push',
|
|
197
|
-
'db:seed': 'pal db:seed',
|
|
198
|
-
},
|
|
199
|
-
dependencies: {
|
|
200
|
-
'@paljs/core': 'workspace:*',
|
|
201
|
-
'@paljs/orm': 'workspace:*',
|
|
202
|
-
'@paljs/http': 'workspace:*',
|
|
203
|
-
},
|
|
204
|
-
devDependencies: {
|
|
205
|
-
'typescript': '^5.0.0',
|
|
206
|
-
'@types/node': '^20.0.0',
|
|
207
|
-
'tsx': '^4.0.0',
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
const tsconfig = {
|
|
211
|
-
compilerOptions: {
|
|
212
|
-
target: 'ES2022',
|
|
213
|
-
module: 'ESNext',
|
|
214
|
-
moduleResolution: 'bundler',
|
|
215
|
-
experimentalDecorators: true,
|
|
216
|
-
emitDecoratorMetadata: true,
|
|
217
|
-
strict: true,
|
|
218
|
-
esModuleInterop: true,
|
|
219
|
-
skipLibCheck: true,
|
|
220
|
-
outDir: 'dist',
|
|
221
|
-
},
|
|
222
|
-
include: ['src/**/*'],
|
|
223
|
-
};
|
|
224
|
-
writeFile(path.join(dir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
225
|
-
writeFile(path.join(dir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
226
|
-
writeFile(path.join(dir, 'src', 'index.ts'), '// Welcome to Pal!\n\n');
|
|
227
|
-
console.log(`✅ Project created: ${name}/`);
|
|
228
|
-
console.log(` Run: cd ${name} && pnpm install && pnpm dev`);
|
|
229
|
-
}
|
|
230
|
-
function dbPush() {
|
|
231
|
-
console.log('🔄 Pushing schema to database...');
|
|
232
|
-
console.log(' (This would sync your models to the database)');
|
|
234
|
+
const code = generateValidator(name);
|
|
235
|
+
writeFile(path.join(dir, `${fileName}.ts`), code);
|
|
236
|
+
console.log(`✅ Created validator: app/validators/${fileName}.ts`);
|
|
237
|
+
}
|
|
238
|
+
function dbMigrate() {
|
|
239
|
+
console.log('🔄 Running database migrations...');
|
|
240
|
+
console.log(' (This would run all pending migrations)');
|
|
241
|
+
}
|
|
242
|
+
function dbRollback() {
|
|
243
|
+
console.log('⏪ Rolling back last migration...');
|
|
244
|
+
console.log(' (This would rollback the last migration)');
|
|
233
245
|
}
|
|
234
246
|
function dbSeed() {
|
|
235
247
|
console.log('🌱 Running database seeds...');
|
|
236
248
|
console.log(' (This would seed your database with initial data)');
|
|
237
249
|
}
|
|
250
|
+
function dbFresh() {
|
|
251
|
+
console.log('⚠️ Dropping all tables and re-running migrations...');
|
|
252
|
+
console.log(' (This would drop all tables and run migrations)');
|
|
253
|
+
}
|
|
254
|
+
function routeList() {
|
|
255
|
+
console.log('\n📋 Registered Routes:');
|
|
256
|
+
console.log(' (No routes registered yet)');
|
|
257
|
+
console.log('\n Define routes in: start/routes.ts\n');
|
|
258
|
+
}
|
|
259
|
+
function keyGenerate() {
|
|
260
|
+
const { randomBytes } = require('crypto');
|
|
261
|
+
const key = randomBytes(32).toString('hex');
|
|
262
|
+
console.log(`\n🔑 Application Key: ${key}\n`);
|
|
263
|
+
console.log('Add this to your .env file:');
|
|
264
|
+
console.log(`APP_KEY=${key}\n`);
|
|
265
|
+
}
|
|
266
|
+
function listRoutes() {
|
|
267
|
+
routeList();
|
|
268
|
+
}
|
|
238
269
|
function parseOptions(args) {
|
|
239
270
|
return args
|
|
240
271
|
.filter(a => a.startsWith('--'))
|
|
@@ -265,180 +296,157 @@ function writeFile(filePath, content) {
|
|
|
265
296
|
}
|
|
266
297
|
fs.writeFileSync(filePath, content);
|
|
267
298
|
}
|
|
268
|
-
function
|
|
299
|
+
function generateModel(name, options) {
|
|
269
300
|
const camelName = toCamelCase(name);
|
|
270
301
|
const hasResource = options.resource;
|
|
271
|
-
return `import { BaseEntity } from '@
|
|
302
|
+
return `import { BaseEntity } from '@rpal/orm';
|
|
272
303
|
|
|
273
304
|
export interface ${name} extends BaseEntity {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
// createdAt: Date;
|
|
282
|
-
// updatedAt: Date;` : ''}
|
|
305
|
+
${hasResource ? `name: string;
|
|
306
|
+
email: string;
|
|
307
|
+
status: 'active' | 'inactive';
|
|
308
|
+
description?: string;` : `// Define your ${name} model fields here
|
|
309
|
+
// Example:
|
|
310
|
+
// name: string;
|
|
311
|
+
// email?: string;`}
|
|
283
312
|
}
|
|
284
313
|
|
|
285
314
|
export interface Create${name}DTO {
|
|
286
|
-
name: string;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
315
|
+
${hasResource ? `name: string;
|
|
316
|
+
email: string;
|
|
317
|
+
description?: string;` : `// Define create DTO fields here
|
|
318
|
+
// Example:
|
|
319
|
+
// name: string;`}
|
|
290
320
|
}
|
|
291
321
|
|
|
292
322
|
export interface Update${name}DTO {
|
|
293
|
-
name?: string;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
323
|
+
${hasResource ? `name?: string;
|
|
324
|
+
email?: string;
|
|
325
|
+
status?: 'active' | 'inactive';
|
|
326
|
+
description?: string;` : `// Define update DTO fields here
|
|
327
|
+
// Example:
|
|
328
|
+
// name?: string;`}
|
|
297
329
|
}
|
|
298
|
-
`;
|
|
299
|
-
}
|
|
300
|
-
function generateRepository(name, options) {
|
|
301
|
-
const camelName = toCamelCase(name);
|
|
302
|
-
const hasResource = options.resource;
|
|
303
|
-
let extraMethods = '';
|
|
304
|
-
if (hasResource) {
|
|
305
|
-
extraMethods = `
|
|
306
|
-
async findByEmail(email: string): Promise<${name} | null> {
|
|
307
|
-
return this.findOne({ email });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async findActive(): Promise<${name}[] | null> {
|
|
311
|
-
return this.findAll({ status: 'active' });
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async exists(email: string, excludeId?: string): Promise<boolean> {
|
|
315
|
-
const existing = await this.findByEmail(email);
|
|
316
|
-
if (!existing) return false;
|
|
317
|
-
if (excludeId && existing.id === excludeId) return false;
|
|
318
|
-
return true;
|
|
319
|
-
}`;
|
|
320
|
-
}
|
|
321
|
-
return `import { BaseRepository } from '@paljs/orm';
|
|
322
|
-
import { ${name}, Create${name}DTO, Update${name}DTO } from './index';
|
|
323
330
|
|
|
324
|
-
export
|
|
325
|
-
protected toEntity(data: Create${name}DTO): ${name} {
|
|
326
|
-
const now = new Date();
|
|
327
|
-
return {
|
|
328
|
-
id: this.generateId(),
|
|
329
|
-
name: data.name,
|
|
330
|
-
${hasResource ? `// email: data.email,
|
|
331
|
-
// status: data.status || 'active',` : ''}
|
|
332
|
-
createdAt: now,
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
protected toUpdateData(data: Update${name}DTO): Partial<${name}> {
|
|
337
|
-
return {
|
|
338
|
-
name: data.name,
|
|
339
|
-
${hasResource ? `// email: data.email,
|
|
340
|
-
// status: data.status,
|
|
341
|
-
// updatedAt: new Date(),` : ''}
|
|
342
|
-
};
|
|
343
|
-
}${extraMethods}
|
|
344
|
-
}
|
|
331
|
+
export const ${name}Token = Symbol('${name}');
|
|
345
332
|
`;
|
|
346
333
|
}
|
|
347
334
|
function generateService(name) {
|
|
348
335
|
const camelName = toCamelCase(name);
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
import {
|
|
352
|
-
import {
|
|
336
|
+
const modelName = `${name}Model`;
|
|
337
|
+
const modelFileName = toKebabCase(name);
|
|
338
|
+
return `import { Injectable, Inject } from '@rpal/core';
|
|
339
|
+
import { ${modelName}, ${modelName}Token } from '#models/${modelFileName}';
|
|
353
340
|
|
|
354
341
|
@Injectable()
|
|
355
342
|
export class ${name}Service {
|
|
356
|
-
constructor(
|
|
343
|
+
constructor(
|
|
344
|
+
@Inject(${modelName}Token) private ${camelName}Model: ${modelName}
|
|
345
|
+
) {}
|
|
357
346
|
|
|
358
347
|
async findAll() {
|
|
359
|
-
return this.${camelName}
|
|
348
|
+
return this.${camelName}Model.findAll();
|
|
360
349
|
}
|
|
361
350
|
|
|
362
|
-
async
|
|
363
|
-
return this.${camelName}
|
|
351
|
+
async findById(id: string) {
|
|
352
|
+
return this.${camelName}Model.findById(id);
|
|
364
353
|
}
|
|
365
354
|
|
|
366
|
-
async create(data:
|
|
367
|
-
return this.${camelName}
|
|
355
|
+
async create(data: Partial<${modelName}>) {
|
|
356
|
+
return this.${camelName}Model.create(data);
|
|
368
357
|
}
|
|
369
358
|
|
|
370
|
-
async update(id: string, data:
|
|
371
|
-
return this.${camelName}
|
|
359
|
+
async update(id: string, data: Partial<${modelName}>) {
|
|
360
|
+
return this.${camelName}Model.update(id, data);
|
|
372
361
|
}
|
|
373
362
|
|
|
374
363
|
async delete(id: string) {
|
|
375
|
-
return this.${camelName}
|
|
364
|
+
return this.${camelName}Model.delete(id);
|
|
376
365
|
}
|
|
377
366
|
}
|
|
378
367
|
`;
|
|
379
368
|
}
|
|
380
369
|
function generateController(name, options) {
|
|
381
370
|
const camelName = toCamelCase(name);
|
|
371
|
+
const serviceName = `${name}Service`;
|
|
372
|
+
const fileName = toKebabCase(name);
|
|
382
373
|
const hasApi = options.api;
|
|
383
374
|
const hasResource = options.resource;
|
|
384
|
-
const kebabName = toKebabCase(name);
|
|
385
375
|
let crudMethods = '';
|
|
386
376
|
if (hasApi) {
|
|
387
377
|
crudMethods = `
|
|
388
|
-
async index() {
|
|
389
|
-
const
|
|
390
|
-
return { data
|
|
378
|
+
async index({ request, response }: HttpContext) {
|
|
379
|
+
const data = await this.${camelName}Service.findAll();
|
|
380
|
+
return response.json({ data });
|
|
391
381
|
}
|
|
392
382
|
|
|
393
|
-
async show(
|
|
394
|
-
const ${camelName} = await this.${camelName}Service.
|
|
383
|
+
async show({ params, response }: HttpContext) {
|
|
384
|
+
const ${camelName} = await this.${camelName}Service.findById(params.id);
|
|
395
385
|
if (!${camelName}) {
|
|
396
|
-
|
|
386
|
+
return response.notFound({ message: '${name} not found' });
|
|
397
387
|
}
|
|
398
|
-
return { data: ${camelName} };
|
|
388
|
+
return response.json({ data: ${camelName} });
|
|
399
389
|
}
|
|
400
390
|
|
|
401
|
-
async store(
|
|
391
|
+
async store({ request, response }: HttpContext) {
|
|
392
|
+
const data = request.body();
|
|
402
393
|
const ${camelName} = await this.${camelName}Service.create(data);
|
|
403
|
-
return { data: ${camelName}
|
|
394
|
+
return response.created({ data: ${camelName} });
|
|
404
395
|
}
|
|
405
396
|
|
|
406
|
-
async update(
|
|
407
|
-
const
|
|
408
|
-
|
|
397
|
+
async update({ params, request, response }: HttpContext) {
|
|
398
|
+
const data = request.body();
|
|
399
|
+
const ${camelName} = await this.${camelName}Service.update(params.id, data);
|
|
400
|
+
if (!${camelName}) {
|
|
401
|
+
return response.notFound({ message: '${name} not found' });
|
|
402
|
+
}
|
|
403
|
+
return response.json({ data: ${camelName} });
|
|
409
404
|
}
|
|
410
405
|
|
|
411
|
-
async destroy(
|
|
412
|
-
await this.${camelName}Service.delete(id);
|
|
413
|
-
return
|
|
406
|
+
async destroy({ params, response }: HttpContext) {
|
|
407
|
+
await this.${camelName}Service.delete(params.id);
|
|
408
|
+
return response.noContent();
|
|
414
409
|
}`;
|
|
415
410
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
import { ${
|
|
411
|
+
return `import type { HttpContext } from '@rpal/http';
|
|
412
|
+
import { Injectable } from '@rpal/core';
|
|
413
|
+
import { ${serviceName} } from '#services/${fileName}.service';
|
|
419
414
|
|
|
420
415
|
@Injectable()
|
|
421
416
|
export class ${name}Controller {
|
|
422
|
-
constructor(private ${camelName}Service: ${
|
|
417
|
+
constructor(private ${camelName}Service: ${serviceName}) {}${crudMethods}
|
|
423
418
|
}
|
|
424
419
|
`;
|
|
425
|
-
|
|
426
|
-
|
|
420
|
+
}
|
|
421
|
+
function generateMiddleware(name) {
|
|
422
|
+
const fileName = toKebabCase(name);
|
|
423
|
+
return `import type { HttpContext } from '@rpal/http';
|
|
424
|
+
import { Middleware } from '@rpal/http';
|
|
427
425
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
426
|
+
export class ${name}Middleware implements Middleware {
|
|
427
|
+
async handle({ request, response }: HttpContext, next: () => Promise<void>) {
|
|
428
|
+
// Add your middleware logic here
|
|
429
|
+
console.log('${name}Middleware: Processing request', request.url());
|
|
430
|
+
|
|
431
|
+
// Example: Check for authorization header
|
|
432
|
+
// const authHeader = request.header('Authorization');
|
|
433
|
+
// if (!authHeader) {
|
|
434
|
+
// return response.unauthorized({ message: 'Unauthorized' });
|
|
435
|
+
// }
|
|
436
|
+
|
|
437
|
+
await next();
|
|
432
438
|
}
|
|
433
439
|
}
|
|
434
440
|
`;
|
|
435
441
|
}
|
|
436
442
|
function generateMigration(name) {
|
|
437
|
-
const tableName = toKebabCase(name)
|
|
438
|
-
return `import { Migration } from '@
|
|
443
|
+
const tableName = toKebabCase(name);
|
|
444
|
+
return `import type { Migration } from '@rpal/orm';
|
|
439
445
|
|
|
440
|
-
export class
|
|
441
|
-
|
|
446
|
+
export default class ${toPascalCase(name)}Migration implements Migration {
|
|
447
|
+
name = '${Date.now()}_${tableName}';
|
|
448
|
+
|
|
449
|
+
async up() {
|
|
442
450
|
return \`
|
|
443
451
|
CREATE TABLE ${tableName} (
|
|
444
452
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -449,20 +457,55 @@ export class Create${name}Table implements Migration {
|
|
|
449
457
|
\`;
|
|
450
458
|
}
|
|
451
459
|
|
|
452
|
-
down() {
|
|
453
|
-
return \`DROP TABLE ${tableName}\`;
|
|
460
|
+
async down() {
|
|
461
|
+
return \`DROP TABLE IF EXISTS ${tableName}\`;
|
|
454
462
|
}
|
|
455
463
|
}
|
|
456
464
|
`;
|
|
457
465
|
}
|
|
458
466
|
function generateFactory(name) {
|
|
459
|
-
|
|
460
|
-
import {
|
|
467
|
+
const modelFileName = toKebabCase(name);
|
|
468
|
+
return `import { Factory } from '@rpal/palu';
|
|
469
|
+
import { ${name}Model } from '#models/${modelFileName}';
|
|
461
470
|
|
|
462
|
-
export const ${name}Factory = Factory.define<${name}>(({ sequence }) => ({
|
|
471
|
+
export const ${name}Factory = Factory.define<${name}Model>(({ sequence }) => ({
|
|
463
472
|
id: \`seed-\${sequence}\`,
|
|
464
473
|
name: \`Test ${name} \${sequence}\`,
|
|
465
474
|
createdAt: new Date(),
|
|
466
475
|
}));
|
|
467
476
|
`;
|
|
468
477
|
}
|
|
478
|
+
function generateException(name) {
|
|
479
|
+
return `import { Exception } from '@rpal/http';
|
|
480
|
+
|
|
481
|
+
export class ${name} extends Exception {
|
|
482
|
+
constructor(
|
|
483
|
+
message: string = '${name} error',
|
|
484
|
+
status: number = 500
|
|
485
|
+
) {
|
|
486
|
+
super(message, status);
|
|
487
|
+
this.name = '${name}';
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
`;
|
|
491
|
+
}
|
|
492
|
+
function generateValidator(name) {
|
|
493
|
+
const modelFileName = toKebabCase(name).replace('-', '');
|
|
494
|
+
return `import { z } from 'zod';
|
|
495
|
+
|
|
496
|
+
export const ${name}Schema = z.object({
|
|
497
|
+
// Define your validation schema here
|
|
498
|
+
// Example:
|
|
499
|
+
// name: z.string().min(1).max(255),
|
|
500
|
+
// email: z.string().email(),
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
export type ${name}Input = z.infer<typeof ${name}Schema>;
|
|
504
|
+
`;
|
|
505
|
+
}
|
|
506
|
+
function toPascalCase(str) {
|
|
507
|
+
return str
|
|
508
|
+
.split(/[-_\s]+/)
|
|
509
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
510
|
+
.join('');
|
|
511
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const templates = [
|
|
2
|
+
{
|
|
3
|
+
name: 'API',
|
|
4
|
+
alias: 'api',
|
|
5
|
+
hint: 'Type-safe REST API with authentication and database',
|
|
6
|
+
source: 'github:paljs/starter-kits/api',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'Admin',
|
|
10
|
+
alias: 'admin',
|
|
11
|
+
hint: 'Admin dashboard with authentication and UI components',
|
|
12
|
+
source: 'github:paljs/starter-kits/admin',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'Client',
|
|
16
|
+
alias: 'client',
|
|
17
|
+
hint: 'Full-stack client application with frontend and API',
|
|
18
|
+
source: 'github:paljs/starter-kits/client',
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
export function getTemplateByAlias(alias) {
|
|
22
|
+
return templates.find((t) => t.alias === alias);
|
|
23
|
+
}
|
|
24
|
+
export function getTemplateByName(name) {
|
|
25
|
+
return templates.find((t) => t.name.toLowerCase() === name.toLowerCase());
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpal/cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI tool for PalJS",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "CLI tool for PalJS - The batteries-included full-stack framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"dev": "tsx src/index.ts"
|
|
22
|
+
},
|
|
19
23
|
"dependencies": {
|
|
20
|
-
"@rpal/core": "
|
|
24
|
+
"@rpal/core": "workspace:*"
|
|
21
25
|
},
|
|
22
26
|
"devDependencies": {
|
|
23
27
|
"@types/node": "^20.0.0",
|
|
24
28
|
"tsx": "^4.0.0"
|
|
25
|
-
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsc -p tsconfig.json",
|
|
28
|
-
"dev": "tsx src/index.ts"
|
|
29
29
|
}
|
|
30
|
-
}
|
|
30
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 paljs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|