@kargojs/cli 0.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/bin/kargo.js +7 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +48 -0
- package/dist/commands/make-panel.d.ts +7 -0
- package/dist/commands/make-panel.d.ts.map +1 -0
- package/dist/commands/make-panel.js +39 -0
- package/dist/commands/make-resource.d.ts +14 -0
- package/dist/commands/make-resource.d.ts.map +1 -0
- package/dist/commands/make-resource.js +101 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/templates/database.d.ts +13 -0
- package/dist/templates/database.d.ts.map +1 -0
- package/dist/templates/database.js +43 -0
- package/dist/templates/form.d.ts +3 -0
- package/dist/templates/form.d.ts.map +1 -0
- package/dist/templates/form.js +11 -0
- package/dist/templates/project.d.ts +7 -0
- package/dist/templates/project.d.ts.map +1 -0
- package/dist/templates/project.js +459 -0
- package/dist/templates/resource.d.ts +3 -0
- package/dist/templates/resource.d.ts.map +1 -0
- package/dist/templates/resource.js +15 -0
- package/dist/templates/service-in-memory.d.ts +3 -0
- package/dist/templates/service-in-memory.d.ts.map +1 -0
- package/dist/templates/service-in-memory.js +47 -0
- package/dist/templates/service.d.ts +3 -0
- package/dist/templates/service.d.ts.map +1 -0
- package/dist/templates/service.js +21 -0
- package/dist/templates/table.d.ts +3 -0
- package/dist/templates/table.d.ts.map +1 -0
- package/dist/templates/table.js +31 -0
- package/dist/utils/fs.d.ts +12 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +26 -0
- package/dist/utils/log.d.ts +11 -0
- package/dist/utils/log.d.ts.map +1 -0
- package/dist/utils/log.js +16 -0
- package/dist/utils/register-resource.d.ts +22 -0
- package/dist/utils/register-resource.d.ts.map +1 -0
- package/dist/utils/register-resource.js +71 -0
- package/dist/utils/strings.d.ts +31 -0
- package/dist/utils/strings.d.ts.map +1 -0
- package/dist/utils/strings.js +63 -0
- package/package.json +31 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { getDbConfig } from './database.js';
|
|
2
|
+
/** All files generated by `kargo create <name>`. Returns an array of {path, content}. */
|
|
3
|
+
export function projectFiles(name, database = 'sqlite') {
|
|
4
|
+
const base = name;
|
|
5
|
+
const db = getDbConfig(database);
|
|
6
|
+
return [
|
|
7
|
+
// ── Root ──────────────────────────────────────────────────────────────
|
|
8
|
+
{
|
|
9
|
+
path: `${base}/package.json`,
|
|
10
|
+
content: JSON.stringify({
|
|
11
|
+
name,
|
|
12
|
+
private: true,
|
|
13
|
+
scripts: {
|
|
14
|
+
build: 'turbo run build',
|
|
15
|
+
dev: 'turbo run dev',
|
|
16
|
+
lint: 'turbo run lint',
|
|
17
|
+
},
|
|
18
|
+
devDependencies: {
|
|
19
|
+
turbo: '^2.0.0',
|
|
20
|
+
typescript: '7.0.2',
|
|
21
|
+
},
|
|
22
|
+
workspaces: ['apps/*'],
|
|
23
|
+
}, null, 2) + '\n',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: `${base}/turbo.json`,
|
|
27
|
+
content: JSON.stringify({
|
|
28
|
+
$schema: 'https://turbo.build/schema.json',
|
|
29
|
+
tasks: {
|
|
30
|
+
build: { dependsOn: ['^build'], outputs: ['dist/**'] },
|
|
31
|
+
dev: { persistent: true, cache: false },
|
|
32
|
+
lint: {},
|
|
33
|
+
},
|
|
34
|
+
}, null, 2) + '\n',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: `${base}/.gitignore`,
|
|
38
|
+
content: [
|
|
39
|
+
'node_modules/',
|
|
40
|
+
'dist/',
|
|
41
|
+
'.turbo/',
|
|
42
|
+
'.next/',
|
|
43
|
+
'*.env',
|
|
44
|
+
'*.env.local',
|
|
45
|
+
'*.db',
|
|
46
|
+
'*.db-journal',
|
|
47
|
+
'',
|
|
48
|
+
].join('\n'),
|
|
49
|
+
},
|
|
50
|
+
// ── NestJS API ────────────────────────────────────────────────────────
|
|
51
|
+
{
|
|
52
|
+
path: `${base}/apps/api/package.json`,
|
|
53
|
+
content: JSON.stringify({
|
|
54
|
+
name: `${name}-api`,
|
|
55
|
+
type: 'module',
|
|
56
|
+
private: true,
|
|
57
|
+
scripts: {
|
|
58
|
+
build: 'tsc',
|
|
59
|
+
dev: 'tsx src/main.ts',
|
|
60
|
+
start: 'node dist/main.js',
|
|
61
|
+
postinstall: 'prisma generate',
|
|
62
|
+
'db:migrate': 'prisma migrate dev',
|
|
63
|
+
'db:seed': 'tsx prisma/seed.ts',
|
|
64
|
+
},
|
|
65
|
+
dependencies: {
|
|
66
|
+
'@kargojs/core': 'latest',
|
|
67
|
+
'@kargojs/forms': 'latest',
|
|
68
|
+
'@kargojs/nestjs': 'latest',
|
|
69
|
+
'@kargojs/prisma': 'latest',
|
|
70
|
+
'@kargojs/tables': 'latest',
|
|
71
|
+
'@nestjs/common': '^12.0.0',
|
|
72
|
+
'@nestjs/core': '^12.0.0',
|
|
73
|
+
'@nestjs/platform-express': '^12.0.0',
|
|
74
|
+
[db.adapterPackage]: '^7.10.0',
|
|
75
|
+
'@prisma/client': '^7.10.0',
|
|
76
|
+
'reflect-metadata': '^0.2.0',
|
|
77
|
+
rxjs: '^7.8.0',
|
|
78
|
+
},
|
|
79
|
+
devDependencies: {
|
|
80
|
+
'@types/express': '^5.0.0',
|
|
81
|
+
'@types/node': '^22.0.0',
|
|
82
|
+
dotenv: '^16.4.5',
|
|
83
|
+
prisma: '^7.10.0',
|
|
84
|
+
tsx: '^4.19.0',
|
|
85
|
+
typescript: '7.0.2',
|
|
86
|
+
},
|
|
87
|
+
}, null, 2) + '\n',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
path: `${base}/apps/api/tsconfig.json`,
|
|
91
|
+
content: JSON.stringify({
|
|
92
|
+
compilerOptions: {
|
|
93
|
+
target: 'ES2022',
|
|
94
|
+
module: 'NodeNext',
|
|
95
|
+
moduleResolution: 'NodeNext',
|
|
96
|
+
strict: true,
|
|
97
|
+
skipLibCheck: true,
|
|
98
|
+
esModuleInterop: true,
|
|
99
|
+
experimentalDecorators: true,
|
|
100
|
+
emitDecoratorMetadata: true,
|
|
101
|
+
outDir: 'dist',
|
|
102
|
+
rootDir: 'src',
|
|
103
|
+
types: ['node'],
|
|
104
|
+
},
|
|
105
|
+
include: ['src'],
|
|
106
|
+
exclude: ['node_modules', 'dist'],
|
|
107
|
+
}, null, 2) + '\n',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
path: `${base}/apps/api/prisma.config.ts`,
|
|
111
|
+
content: `import 'dotenv/config';
|
|
112
|
+
import { defineConfig, env } from 'prisma/config';
|
|
113
|
+
|
|
114
|
+
export default defineConfig({
|
|
115
|
+
schema: 'prisma/schema.prisma',
|
|
116
|
+
migrations: {
|
|
117
|
+
path: 'prisma/migrations',
|
|
118
|
+
seed: 'tsx prisma/seed.ts',
|
|
119
|
+
},
|
|
120
|
+
datasource: {
|
|
121
|
+
url: env('DATABASE_URL'),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
`,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
path: `${base}/apps/api/.env`,
|
|
128
|
+
content: db.envContent,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
path: `${base}/apps/api/prisma/schema.prisma`,
|
|
132
|
+
content: `generator client {
|
|
133
|
+
provider = "prisma-client-js"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
datasource db {
|
|
137
|
+
// Connection config lives in prisma.config.ts / the driver adapter at
|
|
138
|
+
// runtime (src/prisma.service.ts) — schema-level \`url\` isn't supported.
|
|
139
|
+
provider = "${db.prismaProvider}"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
model Example {
|
|
143
|
+
id Int @id @default(autoincrement())
|
|
144
|
+
name String
|
|
145
|
+
description String
|
|
146
|
+
createdAt DateTime @default(now())
|
|
147
|
+
}
|
|
148
|
+
`,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
path: `${base}/apps/api/prisma/seed.ts`,
|
|
152
|
+
content: `import { PrismaClient } from '@prisma/client';
|
|
153
|
+
import { ${db.adapterClass} } from '${db.adapterPackage}';
|
|
154
|
+
|
|
155
|
+
const adapter = ${db.adapterConstructor};
|
|
156
|
+
const prisma = new PrismaClient({ adapter });
|
|
157
|
+
|
|
158
|
+
async function main() {
|
|
159
|
+
await prisma.example.createMany({
|
|
160
|
+
data: [
|
|
161
|
+
{ name: 'First record', description: 'Hello from Kargo' },
|
|
162
|
+
{ name: 'Second record', description: 'Edit or delete me from the admin panel' },
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
console.log('Seeded example records.');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
main()
|
|
169
|
+
.catch((e) => { console.error(e); process.exit(1); })
|
|
170
|
+
.finally(() => prisma.$disconnect());
|
|
171
|
+
`,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
path: `${base}/apps/api/src/main.ts`,
|
|
175
|
+
content: `import 'reflect-metadata';
|
|
176
|
+
import { NestFactory } from '@nestjs/core';
|
|
177
|
+
import { AppModule } from './app.module.js';
|
|
178
|
+
|
|
179
|
+
async function bootstrap() {
|
|
180
|
+
const app = await NestFactory.create(AppModule);
|
|
181
|
+
app.enableCors();
|
|
182
|
+
await app.listen(3001);
|
|
183
|
+
console.log('Kargo API running on http://localhost:3001');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void bootstrap();
|
|
187
|
+
`,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
path: `${base}/apps/api/src/panel.ts`,
|
|
191
|
+
content: `import { Panel } from '@kargojs/core';
|
|
192
|
+
|
|
193
|
+
export const adminPanel = Panel.make({
|
|
194
|
+
id: 'admin',
|
|
195
|
+
path: '/admin',
|
|
196
|
+
globalSearch: true,
|
|
197
|
+
});
|
|
198
|
+
`,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
path: `${base}/apps/api/src/prisma.service.ts`,
|
|
202
|
+
content: `import { Injectable, type OnModuleInit, type OnModuleDestroy } from '@nestjs/common';
|
|
203
|
+
import { PrismaClient } from '@prisma/client';
|
|
204
|
+
import { ${db.adapterClass} } from '${db.adapterPackage}';
|
|
205
|
+
|
|
206
|
+
// Prisma generates its client per-project against this project's own
|
|
207
|
+
// schema.prisma, so this small wrapper lives here rather than in a shared
|
|
208
|
+
// package — see @kargojs/prisma's PrismaResourceService for the reusable
|
|
209
|
+
// generic CRUD layer built on top of whatever delegate this client exposes.
|
|
210
|
+
const adapter = ${db.adapterConstructor};
|
|
211
|
+
|
|
212
|
+
@Injectable()
|
|
213
|
+
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
214
|
+
constructor() {
|
|
215
|
+
super({ adapter });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async onModuleInit(): Promise<void> {
|
|
219
|
+
await this.$connect();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async onModuleDestroy(): Promise<void> {
|
|
223
|
+
await this.$disconnect();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
`,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
path: `${base}/apps/api/src/prisma.module.ts`,
|
|
230
|
+
content: `import { Global, Module } from '@nestjs/common';
|
|
231
|
+
import { PrismaService } from './prisma.service.js';
|
|
232
|
+
|
|
233
|
+
// @Global() so PrismaService resolves inside KargoModule.forRoot()'s
|
|
234
|
+
// dynamically-registered resource services too, not just AppModule's own
|
|
235
|
+
// provider scope — those services live in a separate module boundary that
|
|
236
|
+
// KargoModule.forRoot() constructs at runtime and never imports this module.
|
|
237
|
+
@Global()
|
|
238
|
+
@Module({
|
|
239
|
+
providers: [PrismaService],
|
|
240
|
+
exports: [PrismaService],
|
|
241
|
+
})
|
|
242
|
+
export class PrismaModule {}
|
|
243
|
+
`,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
path: `${base}/apps/api/src/app.module.ts`,
|
|
247
|
+
content: `import 'reflect-metadata';
|
|
248
|
+
import { Module } from '@nestjs/common';
|
|
249
|
+
import { KargoModule } from '@kargojs/nestjs';
|
|
250
|
+
import { adminPanel } from './panel.js';
|
|
251
|
+
import { PrismaModule } from './prisma.module.js';
|
|
252
|
+
import { ExampleResource } from './resources/example/example.resource.js';
|
|
253
|
+
import { ExampleService } from './services/example.service.js';
|
|
254
|
+
|
|
255
|
+
@Module({
|
|
256
|
+
imports: [
|
|
257
|
+
PrismaModule,
|
|
258
|
+
KargoModule.forRoot({
|
|
259
|
+
panel: adminPanel,
|
|
260
|
+
resources: [
|
|
261
|
+
{ resource: ExampleResource, service: ExampleService },
|
|
262
|
+
],
|
|
263
|
+
}),
|
|
264
|
+
],
|
|
265
|
+
})
|
|
266
|
+
export class AppModule {}
|
|
267
|
+
`,
|
|
268
|
+
},
|
|
269
|
+
// ── Split Resource / Form / Table (Filament-style single responsibility) ──
|
|
270
|
+
{
|
|
271
|
+
path: `${base}/apps/api/src/resources/example/example.resource.ts`,
|
|
272
|
+
content: `import { Resource } from '@kargojs/core';
|
|
273
|
+
import { exampleForm } from './example.form.js';
|
|
274
|
+
import { exampleTable } from './example.table.js';
|
|
275
|
+
|
|
276
|
+
export class ExampleResource extends Resource {
|
|
277
|
+
static readonly navigationLabel = 'Examples';
|
|
278
|
+
static readonly navigationIcon = 'document';
|
|
279
|
+
|
|
280
|
+
static form() { return exampleForm(); }
|
|
281
|
+
static table() { return exampleTable(); }
|
|
282
|
+
}
|
|
283
|
+
`,
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
path: `${base}/apps/api/src/resources/example/example.form.ts`,
|
|
287
|
+
content: `import { FormSchema } from '@kargojs/core';
|
|
288
|
+
import { TextInput } from '@kargojs/forms';
|
|
289
|
+
|
|
290
|
+
export function exampleForm(): FormSchema {
|
|
291
|
+
return FormSchema.make([
|
|
292
|
+
TextInput.make('name').label('Name').required(),
|
|
293
|
+
TextInput.make('description').label('Description'),
|
|
294
|
+
]).columns(2);
|
|
295
|
+
}
|
|
296
|
+
`,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
path: `${base}/apps/api/src/resources/example/example.table.ts`,
|
|
300
|
+
content: `import { TableSchema } from '@kargojs/core';
|
|
301
|
+
import { TextColumn, DateColumn } from '@kargojs/tables';
|
|
302
|
+
import { EditAction, DeleteAction } from '@kargojs/core';
|
|
303
|
+
|
|
304
|
+
export function exampleTable(): TableSchema {
|
|
305
|
+
return TableSchema.make([
|
|
306
|
+
TextColumn.make('id').label('ID').sortable(),
|
|
307
|
+
TextColumn.make('name').label('Name').searchable().sortable(),
|
|
308
|
+
DateColumn.make('createdAt').label('Created').sortable().since(),
|
|
309
|
+
])
|
|
310
|
+
.actions([EditAction.make(), DeleteAction.make()])
|
|
311
|
+
.searchable()
|
|
312
|
+
.paginated();
|
|
313
|
+
}
|
|
314
|
+
`,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
path: `${base}/apps/api/src/services/example.service.ts`,
|
|
318
|
+
content: `import 'reflect-metadata';
|
|
319
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
320
|
+
import { PrismaResourceService } from '@kargojs/prisma';
|
|
321
|
+
import { PrismaService } from '../prisma.service.js';
|
|
322
|
+
import { ExampleResource } from '../resources/example/example.resource.js';
|
|
323
|
+
|
|
324
|
+
export interface Example {
|
|
325
|
+
id: number;
|
|
326
|
+
name: string;
|
|
327
|
+
description: string;
|
|
328
|
+
createdAt: Date;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
@Injectable()
|
|
332
|
+
export class ExampleService extends PrismaResourceService<Example> {
|
|
333
|
+
constructor(@Inject(PrismaService) prisma: PrismaService) {
|
|
334
|
+
super(prisma.example, ExampleResource);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
`,
|
|
338
|
+
},
|
|
339
|
+
// ── Next.js web ───────────────────────────────────────────────────────
|
|
340
|
+
{
|
|
341
|
+
path: `${base}/apps/web/package.json`,
|
|
342
|
+
content: JSON.stringify({
|
|
343
|
+
name: `${name}-web`,
|
|
344
|
+
type: 'module',
|
|
345
|
+
private: true,
|
|
346
|
+
scripts: {
|
|
347
|
+
build: 'next build',
|
|
348
|
+
dev: 'next dev --port 3000',
|
|
349
|
+
start: 'next start',
|
|
350
|
+
lint: 'next lint',
|
|
351
|
+
},
|
|
352
|
+
dependencies: {
|
|
353
|
+
'@kargojs/core': 'latest',
|
|
354
|
+
'@kargojs/next': 'latest',
|
|
355
|
+
next: '^16.0.0',
|
|
356
|
+
react: '^19.0.0',
|
|
357
|
+
'react-dom': '^19.0.0',
|
|
358
|
+
},
|
|
359
|
+
devDependencies: {
|
|
360
|
+
'@types/node': '^22.0.0',
|
|
361
|
+
'@types/react': '^19.0.0',
|
|
362
|
+
'@types/react-dom': '^19.0.0',
|
|
363
|
+
typescript: '7.0.2',
|
|
364
|
+
},
|
|
365
|
+
}, null, 2) + '\n',
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
path: `${base}/apps/web/tsconfig.json`,
|
|
369
|
+
content: JSON.stringify({
|
|
370
|
+
compilerOptions: {
|
|
371
|
+
target: 'ES2022',
|
|
372
|
+
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
373
|
+
module: 'ESNext',
|
|
374
|
+
moduleResolution: 'bundler',
|
|
375
|
+
jsx: 'react-jsx',
|
|
376
|
+
strict: true,
|
|
377
|
+
skipLibCheck: true,
|
|
378
|
+
esModuleInterop: true,
|
|
379
|
+
incremental: true,
|
|
380
|
+
plugins: [{ name: 'next' }],
|
|
381
|
+
types: ['node'],
|
|
382
|
+
},
|
|
383
|
+
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
|
|
384
|
+
exclude: ['node_modules'],
|
|
385
|
+
}, null, 2) + '\n',
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
path: `${base}/apps/web/next.config.mjs`,
|
|
389
|
+
content: `/** @type {import('next').NextConfig} */
|
|
390
|
+
const nextConfig = {
|
|
391
|
+
transpilePackages: ['@kargojs/next'],
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
export default nextConfig;
|
|
395
|
+
`,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
path: `${base}/apps/web/app/layout.tsx`,
|
|
399
|
+
content: `import type { ReactNode } from 'react';
|
|
400
|
+
|
|
401
|
+
export const metadata = { title: '${name} Admin' };
|
|
402
|
+
|
|
403
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
404
|
+
return (
|
|
405
|
+
<html lang="en">
|
|
406
|
+
<body style={{ margin: 0, padding: 0, fontFamily: 'system-ui, sans-serif' }}>
|
|
407
|
+
{children}
|
|
408
|
+
</body>
|
|
409
|
+
</html>
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
`,
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
path: `${base}/apps/web/app/admin/layout.tsx`,
|
|
416
|
+
content: `'use client';
|
|
417
|
+
import type { ReactNode } from 'react';
|
|
418
|
+
import { useRouter, usePathname } from 'next/navigation';
|
|
419
|
+
import { KargoAdminLayout } from '@kargojs/next';
|
|
420
|
+
|
|
421
|
+
// Point this at your running NestJS API
|
|
422
|
+
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? 'http://localhost:3001';
|
|
423
|
+
|
|
424
|
+
export default function AdminLayout({ children }: { children: ReactNode }) {
|
|
425
|
+
const router = useRouter();
|
|
426
|
+
const pathname = usePathname();
|
|
427
|
+
const activeSlug = pathname.replace(/^\\/admin\\/?/, '').split('/')[0] || undefined;
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<KargoAdminLayout
|
|
431
|
+
apiBase={API_BASE}
|
|
432
|
+
brandName="${name}"
|
|
433
|
+
activeSlug={activeSlug}
|
|
434
|
+
onNavigate={(slug) => router.push(\`/admin/\${slug}\`)}
|
|
435
|
+
>
|
|
436
|
+
{children}
|
|
437
|
+
</KargoAdminLayout>
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
`,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
path: `${base}/apps/web/app/admin/[[...slug]]/page.tsx`,
|
|
444
|
+
content: `'use client';
|
|
445
|
+
import { use } from 'react';
|
|
446
|
+
import { KargoRouter } from '@kargojs/next';
|
|
447
|
+
|
|
448
|
+
interface Props {
|
|
449
|
+
params: Promise<{ slug?: string[] }>;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export default function AdminPage({ params }: Props) {
|
|
453
|
+
const { slug } = use(params);
|
|
454
|
+
return <KargoRouter slug={slug} basePath="/admin" />;
|
|
455
|
+
}
|
|
456
|
+
`,
|
|
457
|
+
},
|
|
458
|
+
];
|
|
459
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/templates/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAczD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function resourceTemplate(n) {
|
|
2
|
+
return `import { Resource } from '@kargojs/core';
|
|
3
|
+
import { ${n.camel}Form } from './${n.kebab}.form.js';
|
|
4
|
+
import { ${n.camel}Table } from './${n.kebab}.table.js';
|
|
5
|
+
|
|
6
|
+
export class ${n.pascal}Resource extends Resource {
|
|
7
|
+
static readonly navigationLabel = '${n.pluralTitle}';
|
|
8
|
+
static readonly navigationIcon = 'document';
|
|
9
|
+
static readonly recordLabel = 'id';
|
|
10
|
+
|
|
11
|
+
static form() { return ${n.camel}Form(); }
|
|
12
|
+
static table() { return ${n.camel}Table(); }
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-in-memory.d.ts","sourceRoot":"","sources":["../../src/templates/service-in-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CA8ChE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function serviceInMemoryTemplate(n) {
|
|
2
|
+
return `import { Injectable } from '@nestjs/common';
|
|
3
|
+
import {
|
|
4
|
+
KargoResourceService,
|
|
5
|
+
type KargoQuery,
|
|
6
|
+
type KargoPagedResult,
|
|
7
|
+
} from '@kargojs/nestjs';
|
|
8
|
+
|
|
9
|
+
// Replace with your entity / DTO type
|
|
10
|
+
export interface ${n.pascal} {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
createdAt: Date;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Injectable()
|
|
17
|
+
export class ${n.pascal}Service extends KargoResourceService<${n.pascal}> {
|
|
18
|
+
// TODO: inject your repository / ORM client here
|
|
19
|
+
// constructor(@InjectRepository(${n.pascal}) private repo: Repository<${n.pascal}>) { super(); }
|
|
20
|
+
|
|
21
|
+
async findAll(query: KargoQuery): Promise<KargoPagedResult<${n.pascal}>> {
|
|
22
|
+
// TODO: implement list with pagination + sorting + search
|
|
23
|
+
throw new Error('${n.pascal}Service.findAll not implemented');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async findOne(id: string | number): Promise<${n.pascal} | null> {
|
|
27
|
+
// TODO: implement single record fetch
|
|
28
|
+
throw new Error('${n.pascal}Service.findOne not implemented');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async create(data: Partial<${n.pascal}>): Promise<${n.pascal}> {
|
|
32
|
+
// TODO: implement create
|
|
33
|
+
throw new Error('${n.pascal}Service.create not implemented');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async update(id: string | number, data: Partial<${n.pascal}>): Promise<${n.pascal}> {
|
|
37
|
+
// TODO: implement update
|
|
38
|
+
throw new Error('${n.pascal}Service.update not implemented');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async delete(id: string | number): Promise<void> {
|
|
42
|
+
// TODO: implement delete
|
|
43
|
+
throw new Error('${n.pascal}Service.delete not implemented');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/templates/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAoBxD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function serviceTemplate(n) {
|
|
2
|
+
return `import 'reflect-metadata';
|
|
3
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
4
|
+
import { PrismaResourceService } from '@kargojs/prisma';
|
|
5
|
+
import { PrismaService } from '../prisma.service.js';
|
|
6
|
+
import { ${n.pascal}Resource } from '../resources/${n.kebab}/${n.kebab}.resource.js';
|
|
7
|
+
|
|
8
|
+
export interface ${n.pascal} {
|
|
9
|
+
id: number;
|
|
10
|
+
name: string;
|
|
11
|
+
createdAt: Date;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Injectable()
|
|
15
|
+
export class ${n.pascal}Service extends PrismaResourceService<${n.pascal}> {
|
|
16
|
+
constructor(@Inject(PrismaService) prisma: PrismaService) {
|
|
17
|
+
super(prisma.${n.camel}, ${n.pascal}Resource);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/templates/table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CA8BtD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function tableTemplate(n) {
|
|
2
|
+
return `import { TableSchema, EditAction, DeleteAction } from '@kargojs/core';
|
|
3
|
+
import { TextColumn, DateColumn } from '@kargojs/tables';
|
|
4
|
+
// import { ServerAction, ActionResult } from '@kargojs/actions'; // uncomment to add custom actions
|
|
5
|
+
|
|
6
|
+
export function ${n.camel}Table(): TableSchema {
|
|
7
|
+
return TableSchema.make([
|
|
8
|
+
TextColumn.make('id').label('ID').sortable(),
|
|
9
|
+
TextColumn.make('name').label('Name').searchable().sortable(),
|
|
10
|
+
DateColumn.make('createdAt').label('Created').sortable().since(),
|
|
11
|
+
])
|
|
12
|
+
.actions([
|
|
13
|
+
EditAction.make(),
|
|
14
|
+
DeleteAction.make(),
|
|
15
|
+
// Example server action — uncomment and implement the handler:
|
|
16
|
+
// ServerAction.make('archive')
|
|
17
|
+
// .label('Archive')
|
|
18
|
+
// .color('warning')
|
|
19
|
+
// .requiresConfirmation()
|
|
20
|
+
// .confirmationMessage('Archive this record?')
|
|
21
|
+
// .handle(async ({ id, service }) => {
|
|
22
|
+
// const svc = service as ${n.pascal}Service;
|
|
23
|
+
// await svc.archive(id!);
|
|
24
|
+
// return ActionResult.success('Archived successfully');
|
|
25
|
+
// }),
|
|
26
|
+
])
|
|
27
|
+
.searchable()
|
|
28
|
+
.paginated();
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Write a file, creating parent directories as needed. */
|
|
2
|
+
export declare function writeGeneratedFile(filePath: string, content: string, { force }?: {
|
|
3
|
+
force?: boolean | undefined;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
/** Write multiple files in order. */
|
|
6
|
+
export declare function writeFiles(files: Array<{
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}>, opts?: {
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAIA,2DAA2D;AAC3D,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,EAAE,KAAa,EAAE;;CAAK,GACrB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,qCAAqC;AACrC,wBAAsB,UAAU,CAC9B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC/C,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { mkdir, writeFile, access } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { log } from './log.js';
|
|
4
|
+
/** Write a file, creating parent directories as needed. */
|
|
5
|
+
export async function writeGeneratedFile(filePath, content, { force = false } = {}) {
|
|
6
|
+
const abs = resolve(filePath);
|
|
7
|
+
if (!force) {
|
|
8
|
+
try {
|
|
9
|
+
await access(abs);
|
|
10
|
+
log.warn(`Already exists — skipped: ${filePath}`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// file doesn't exist — proceed
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
18
|
+
await writeFile(abs, content, 'utf8');
|
|
19
|
+
log.file(filePath);
|
|
20
|
+
}
|
|
21
|
+
/** Write multiple files in order. */
|
|
22
|
+
export async function writeFiles(files, opts) {
|
|
23
|
+
for (const { path, content } of files) {
|
|
24
|
+
await writeGeneratedFile(path, content, opts);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const log: {
|
|
2
|
+
info: (msg: string) => void;
|
|
3
|
+
success: (msg: string) => void;
|
|
4
|
+
warn: (msg: string) => void;
|
|
5
|
+
error: (msg: string) => void;
|
|
6
|
+
file: (path: string) => void;
|
|
7
|
+
step: (msg: string) => void;
|
|
8
|
+
blank: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function banner(name: string, version: string): void;
|
|
11
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/utils/log.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,GAAG;gBACF,MAAM;mBACH,MAAM;gBACT,MAAM;iBACL,MAAM;iBACN,MAAM;gBACP,MAAM;;CAEnB,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAKnD"}
|