@aventara/cli 1.0.0-rc.0 → 1.0.0-rc.10
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/CHANGELOG.md +461 -0
- package/README.md +101 -44
- package/dist/apply/conflict.confirmer.js +5 -5
- package/dist/catalog/adapter.catalog.generated.js +1 -1
- package/dist/catalog/host-entry.interface.js +10 -0
- package/dist/catalog/host.catalog.generated.js +32 -0
- package/dist/cli.js +29 -16
- package/dist/command/command.parser.js +99 -13
- package/dist/plan/host-plan.interface.js +1 -0
- package/dist/plan/nest-host.planner.js +131 -0
- package/dist/plan/next-host.planner.js +133 -0
- package/dist/plan/project.planner.js +94 -167
- package/dist/project/project.inspector.js +56 -17
- package/dist/run/command.runner.js +37 -19
- package/dist/run/init.orchestrator.js +55 -38
- package/dist/run/new.orchestrator.js +105 -30
- package/dist/run/scaffold.committer.js +1 -1
- package/dist/run/skill.orchestrator.js +54 -0
- package/dist/skill/agent-skill.generated.js +7 -0
- package/dist/skill/agent-skill.planner.js +116 -0
- package/dist/templates/docs.links.js +2 -1
- package/dist/templates/next-server.entry.js +5 -0
- package/dist/templates/prisma7/prisma7-nest.templates.js +242 -0
- package/dist/templates/prisma7/prisma7-next.templates.js +189 -0
- package/dist/templates/prisma7/prisma7-orm.templates.js +180 -0
- package/dist/templates/prisma7/prisma7.templates.js +7 -365
- package/dist/templates/template.registry.js +2 -2
- package/dist/wizard/answer.resolver.js +5 -5
- package/dist/wizard/readline.prompter.js +3 -3
- package/dist/wizard/wizard.questions.js +11 -11
- package/package.json +3 -3
|
@@ -1,370 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { SCAFFOLD_ENTRYPOINT, } from "../template.registry.js";
|
|
5
|
-
const CLIENT_OUTPUT = "src/generated/prisma";
|
|
6
|
-
const SCHEMA = "prisma/schema.prisma";
|
|
7
|
-
const CONFIG_FILES = ["prisma7.config.ts", "prisma.config.ts"];
|
|
8
|
-
const DRIVER_PREFIX = "@prisma/adapter-";
|
|
9
|
-
const NEW_SERVICE = {
|
|
10
|
-
file: "src/prisma.service.ts",
|
|
11
|
-
exportName: "PrismaService",
|
|
12
|
-
};
|
|
13
|
-
const NEW_MODULE = {
|
|
14
|
-
file: "src/prisma.service.ts",
|
|
15
|
-
exportName: "PrismaModule",
|
|
16
|
-
};
|
|
17
|
-
const PROVIDERS = {
|
|
18
|
-
sqlite: {
|
|
19
|
-
driverClass: "PrismaBetterSqlite3",
|
|
20
|
-
driverOptions: "{ url }",
|
|
21
|
-
databaseUrl: "file:./dev.db",
|
|
22
|
-
gitignore: ["/dev.db*"],
|
|
23
|
-
nativeBuilds: ["@prisma/engines", "better-sqlite3", "prisma"],
|
|
24
|
-
},
|
|
25
|
-
postgresql: {
|
|
26
|
-
driverClass: "PrismaPg",
|
|
27
|
-
driverOptions: "{ connectionString: url }",
|
|
28
|
-
databaseUrl: "postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public",
|
|
29
|
-
gitignore: [],
|
|
30
|
-
nativeBuilds: ["@prisma/engines", "prisma"],
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
function specifier(from, to, js) {
|
|
34
|
-
const relative = path.posix.relative(path.posix.dirname(from), to);
|
|
35
|
-
return `${relative.startsWith(".") ? relative : `./${relative}`}${js}`;
|
|
36
|
-
}
|
|
37
|
-
function schema(input) {
|
|
38
|
-
return [
|
|
39
|
-
"generator client {",
|
|
40
|
-
` provider = "${input.entry.generators[0]}"`,
|
|
41
|
-
` output = "../${CLIENT_OUTPUT}"`,
|
|
42
|
-
...(input.moduleKind === "cjs" ? [' moduleFormat = "cjs"'] : []),
|
|
43
|
-
"}",
|
|
44
|
-
"",
|
|
45
|
-
"datasource db {",
|
|
46
|
-
` provider = "${input.provider}"`,
|
|
47
|
-
"}",
|
|
48
|
-
"",
|
|
49
|
-
"model User {",
|
|
50
|
-
" id Int @id @default(autoincrement())",
|
|
51
|
-
" email String @unique",
|
|
52
|
-
" name String?",
|
|
53
|
-
"}",
|
|
54
|
-
"",
|
|
55
|
-
].join("\n");
|
|
56
|
-
}
|
|
57
|
-
function prismaConfig() {
|
|
58
|
-
return [
|
|
59
|
-
"import { existsSync } from 'node:fs';",
|
|
60
|
-
"import { defineConfig } from 'prisma/config';",
|
|
61
|
-
"",
|
|
62
|
-
"// Prisma reads DATABASE_URL from the environment and does not load .env itself.",
|
|
63
|
-
"if (existsSync('.env')) {",
|
|
64
|
-
" process.loadEnvFile('.env');",
|
|
65
|
-
"}",
|
|
66
|
-
"",
|
|
67
|
-
"export default defineConfig({",
|
|
68
|
-
` schema: '${SCHEMA}',`,
|
|
69
|
-
" migrations: {",
|
|
70
|
-
" path: 'prisma/migrations',",
|
|
71
|
-
" },",
|
|
72
|
-
" datasource: {",
|
|
73
|
-
" url: process.env.DATABASE_URL,",
|
|
74
|
-
" },",
|
|
75
|
-
"});",
|
|
76
|
-
"",
|
|
77
|
-
].join("\n");
|
|
78
|
-
}
|
|
79
|
-
function prismaService(input) {
|
|
80
|
-
const provider = PROVIDERS[input.provider];
|
|
81
|
-
const driver = input.entry.drivers[input.provider];
|
|
82
|
-
return [
|
|
83
|
-
"import { Injectable, Module, type OnModuleDestroy } from '@nestjs/common';",
|
|
84
|
-
`import { ${provider.driverClass} } from '${driver}';`,
|
|
85
|
-
`import { PrismaClient } from '${specifier(NEW_SERVICE.file, `${input.clientOutput}/client`, input.js)}';`,
|
|
86
|
-
"",
|
|
87
|
-
"@Injectable()",
|
|
88
|
-
"export class PrismaService extends PrismaClient implements OnModuleDestroy {",
|
|
89
|
-
" constructor() {",
|
|
90
|
-
" const url = process.env.DATABASE_URL;",
|
|
91
|
-
" if (!url) {",
|
|
92
|
-
" // Thrown while Nest builds the application, before it listens.",
|
|
93
|
-
" throw new Error(",
|
|
94
|
-
" 'DATABASE_URL is not set: put it in .env (the start scripts load it) or in the environment.',",
|
|
95
|
-
" );",
|
|
96
|
-
" }",
|
|
97
|
-
` super({ adapter: new ${provider.driverClass}(${provider.driverOptions}) });`,
|
|
98
|
-
" }",
|
|
99
|
-
"",
|
|
100
|
-
" async onModuleDestroy(): Promise<void> {",
|
|
101
|
-
" await this.$disconnect();",
|
|
102
|
-
" }",
|
|
103
|
-
"}",
|
|
104
|
-
"",
|
|
105
|
-
"@Module({ providers: [PrismaService], exports: [PrismaService] })",
|
|
106
|
-
"export class PrismaModule {}",
|
|
107
|
-
"",
|
|
108
|
-
].join("\n");
|
|
109
|
-
}
|
|
110
|
-
function aventaraConfig(input) {
|
|
111
|
-
const file = "src/aventara.config.ts";
|
|
112
|
-
const service = input.service.exportName;
|
|
113
|
-
return [
|
|
114
|
-
`import { createPrismaAdapter } from '${input.entry.adapterPackage}';`,
|
|
115
|
-
`import { discovery } from '${specifier(file, input.artifact.replace(/\.ts$/, ""), input.js)}';`,
|
|
116
|
-
`import type { ${service} } from '${specifier(file, input.service.file.replace(/\.ts$/, ""), input.js)}';`,
|
|
117
|
-
"",
|
|
118
|
-
"/**",
|
|
119
|
-
" * Aventara's configuration: the entrypoint, and the restrictions and",
|
|
120
|
-
" * pipelines you add. AventaraModule (app.module.ts) builds the framework from",
|
|
121
|
-
" * it; `discovery` is regenerated by the aventara:prepare script.",
|
|
122
|
-
" */",
|
|
123
|
-
`export async function aventaraConfig(prisma: ${service}) {`,
|
|
124
|
-
" return {",
|
|
125
|
-
` entrypoint: '${SCAFFOLD_ENTRYPOINT}',`,
|
|
126
|
-
" adapter: await createPrismaAdapter({",
|
|
127
|
-
input.service.clientMember === undefined
|
|
128
|
-
? " client: prisma,"
|
|
129
|
-
: ` client: prisma.${input.service.clientMember},`,
|
|
130
|
-
" discovery,",
|
|
131
|
-
` provider: '${input.provider}',`,
|
|
132
|
-
` driver: '${input.entry.drivers[input.provider]}',`,
|
|
133
|
-
" }),",
|
|
134
|
-
" } as const;",
|
|
135
|
-
"}",
|
|
136
|
-
"",
|
|
137
|
-
].join("\n");
|
|
138
|
-
}
|
|
139
|
-
function prepare(input) {
|
|
140
|
-
return [
|
|
141
|
-
input.entry.bin,
|
|
142
|
-
`--schema ${input.schema}`,
|
|
143
|
-
`--client ${input.clientOutput}`,
|
|
144
|
-
`--provider ${input.provider}`,
|
|
145
|
-
`--driver ${input.entry.drivers[input.provider]}`,
|
|
146
|
-
`--out ${input.artifact}`,
|
|
147
|
-
].join(" ");
|
|
148
|
-
}
|
|
149
|
-
function appModule(service, module, js) {
|
|
150
|
-
const from = "src/app.module.ts";
|
|
151
|
-
const sameFile = service.file === module.file;
|
|
152
|
-
return {
|
|
153
|
-
imports: [
|
|
154
|
-
`import { aventaraConfig } from '${specifier(from, "src/aventara.config", js)}';`,
|
|
155
|
-
...(sameFile
|
|
156
|
-
? [
|
|
157
|
-
`import { ${module.exportName}, ${service.exportName} } from '${specifier(from, service.file.replace(/\.ts$/, ""), js)}';`,
|
|
158
|
-
]
|
|
159
|
-
: [
|
|
160
|
-
`import { ${module.exportName} } from '${specifier(from, module.file.replace(/\.ts$/, ""), js)}';`,
|
|
161
|
-
`import { ${service.exportName} } from '${specifier(from, service.file.replace(/\.ts$/, ""), js)}';`,
|
|
162
|
-
]),
|
|
163
|
-
],
|
|
164
|
-
entries: [
|
|
165
|
-
`${module.exportName},`,
|
|
166
|
-
[
|
|
167
|
-
"AventaraModule.forRootAsync({",
|
|
168
|
-
` imports: [${module.exportName}],`,
|
|
169
|
-
` inject: [${service.exportName}],`,
|
|
170
|
-
` useFactory: async (prisma: ${service.exportName}) => ({`,
|
|
171
|
-
" config: await aventaraConfig(prisma),",
|
|
172
|
-
" }),",
|
|
173
|
-
"}),",
|
|
174
|
-
].join("\n"),
|
|
175
|
-
],
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function scaffold(input) {
|
|
179
|
-
const js = input.moduleKind === "esm" ? ".js" : "";
|
|
180
|
-
const floor = lowestVersionOf(input.entry.range);
|
|
181
|
-
const provider = PROVIDERS[input.provider];
|
|
182
|
-
const driver = input.entry.drivers[input.provider];
|
|
183
|
-
const run = input.packageManager === "pnpm" ? "pnpm exec" : "npx";
|
|
184
|
-
const artifact = "src/generated/aventara/discovery.artifact.ts";
|
|
185
|
-
return {
|
|
186
|
-
files: [
|
|
187
|
-
{ path: SCHEMA, content: schema(input) },
|
|
188
|
-
{ path: "prisma.config.ts", content: prismaConfig() },
|
|
189
|
-
{
|
|
190
|
-
path: NEW_SERVICE.file,
|
|
191
|
-
content: prismaService({ ...input, clientOutput: CLIENT_OUTPUT, js }),
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
path: "src/aventara.config.ts",
|
|
195
|
-
content: aventaraConfig({
|
|
196
|
-
...input,
|
|
197
|
-
service: NEW_SERVICE,
|
|
198
|
-
artifact,
|
|
199
|
-
js,
|
|
200
|
-
}),
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
env: { DATABASE_URL: provider.databaseUrl },
|
|
204
|
-
gitignore: ["/src/generated/", ...provider.gitignore],
|
|
205
|
-
dependencies: { [input.entry.orm.package]: floor, [driver]: floor },
|
|
206
|
-
devDependencies: { prisma: floor },
|
|
207
|
-
prepare: prepare({
|
|
208
|
-
...input,
|
|
209
|
-
schema: SCHEMA,
|
|
210
|
-
clientOutput: CLIENT_OUTPUT,
|
|
211
|
-
artifact,
|
|
212
|
-
}),
|
|
213
|
-
appModule: appModule(NEW_SERVICE, NEW_MODULE, js),
|
|
214
|
-
nativeBuilds: provider.nativeBuilds,
|
|
215
|
-
nextSteps: [
|
|
216
|
-
...(input.provider === "postgresql"
|
|
217
|
-
? ["Set DATABASE_URL in .env to your PostgreSQL database."]
|
|
218
|
-
: []),
|
|
219
|
-
`Create the database tables: ${run} prisma db push`,
|
|
220
|
-
],
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
function reuse(input) {
|
|
224
|
-
const js = input.moduleKind === "esm" ? ".js" : "";
|
|
225
|
-
const { facts } = input;
|
|
226
|
-
return {
|
|
227
|
-
files: [
|
|
228
|
-
...(input.writeService
|
|
229
|
-
? [
|
|
230
|
-
{
|
|
231
|
-
path: NEW_SERVICE.file,
|
|
232
|
-
content: prismaService({
|
|
233
|
-
entry: input.entry,
|
|
234
|
-
provider: facts.provider,
|
|
235
|
-
clientOutput: facts.clientOutput,
|
|
236
|
-
js,
|
|
237
|
-
}),
|
|
238
|
-
},
|
|
239
|
-
]
|
|
240
|
-
: []),
|
|
241
|
-
{
|
|
242
|
-
path: "src/aventara.config.ts",
|
|
243
|
-
content: aventaraConfig({
|
|
244
|
-
entry: input.entry,
|
|
245
|
-
provider: facts.provider,
|
|
246
|
-
service: input.service,
|
|
247
|
-
artifact: input.artifact,
|
|
248
|
-
js,
|
|
249
|
-
}),
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
env: {},
|
|
253
|
-
gitignore: [`/${path.posix.dirname(input.artifact)}/`],
|
|
254
|
-
dependencies: {},
|
|
255
|
-
devDependencies: {},
|
|
256
|
-
prepare: prepare({
|
|
257
|
-
entry: input.entry,
|
|
258
|
-
schema: facts.schema,
|
|
259
|
-
clientOutput: facts.clientOutput,
|
|
260
|
-
provider: facts.provider,
|
|
261
|
-
artifact: input.artifact,
|
|
262
|
-
}),
|
|
263
|
-
appModule: appModule(input.service, input.module, js),
|
|
264
|
-
nativeBuilds: [],
|
|
265
|
-
nextSteps: [],
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
function schemaLocation(directory, manifest) {
|
|
269
|
-
for (const config of CONFIG_FILES) {
|
|
270
|
-
const file = path.join(directory, config);
|
|
271
|
-
if (existsSync(file)) {
|
|
272
|
-
const declared = /\bschema\s*:\s*['"]([^'"]+)['"]/.exec(readFileSync(file, "utf8"))?.[1];
|
|
273
|
-
if (declared !== undefined) {
|
|
274
|
-
return path.posix.normalize(declared);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
const prisma = manifest.prisma;
|
|
279
|
-
return typeof prisma?.schema === "string"
|
|
280
|
-
? path.posix.normalize(prisma.schema)
|
|
281
|
-
: SCHEMA;
|
|
282
|
-
}
|
|
283
|
-
function schemaFiles(directory, schemaPath) {
|
|
284
|
-
const full = path.join(directory, schemaPath);
|
|
285
|
-
if (!existsSync(full)) {
|
|
286
|
-
return [];
|
|
287
|
-
}
|
|
288
|
-
if (!statSync(full).isDirectory()) {
|
|
289
|
-
return [schemaPath];
|
|
290
|
-
}
|
|
291
|
-
return readdirSync(full, { recursive: true, withFileTypes: true })
|
|
292
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith(".prisma"))
|
|
293
|
-
.map((entry) => path
|
|
294
|
-
.relative(directory, path.join(entry.parentPath, entry.name))
|
|
295
|
-
.split(path.sep)
|
|
296
|
-
.join("/"))
|
|
297
|
-
.sort();
|
|
298
|
-
}
|
|
299
|
-
function blocks(text, kind) {
|
|
300
|
-
return [
|
|
301
|
-
...text.matchAll(new RegExp(`^\\s*${kind}\\s+\\w+\\s*\\{([^}]*)\\}`, "gm")),
|
|
302
|
-
].map((match) => match[1]);
|
|
303
|
-
}
|
|
304
|
-
function setting(block, name) {
|
|
305
|
-
return new RegExp(`^\\s*${name}\\s*=\\s*"([^"]*)"`, "m").exec(block)?.[1];
|
|
306
|
-
}
|
|
307
|
-
function readProject(directory, manifest) {
|
|
308
|
-
const schemaPath = schemaLocation(directory, manifest);
|
|
309
|
-
let provider;
|
|
310
|
-
let generator;
|
|
311
|
-
let clientOutput;
|
|
312
|
-
for (const file of schemaFiles(directory, schemaPath)) {
|
|
313
|
-
const text = readFileSync(path.join(directory, file), "utf8").replace(/\/\/.*$/gm, "");
|
|
314
|
-
provider ??= blocks(text, "datasource")
|
|
315
|
-
.map((block) => setting(block, "provider"))
|
|
316
|
-
.find((value) => value !== undefined);
|
|
317
|
-
for (const block of blocks(text, "generator")) {
|
|
318
|
-
const declared = setting(block, "provider");
|
|
319
|
-
const output = setting(block, "output");
|
|
320
|
-
if (generator === undefined || /^prisma-client/.test(declared ?? "")) {
|
|
321
|
-
generator = declared;
|
|
322
|
-
clientOutput =
|
|
323
|
-
output === undefined
|
|
324
|
-
? undefined
|
|
325
|
-
: path.posix.normalize(path.posix.join(path.posix.dirname(file), output));
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const dependencies = [
|
|
330
|
-
...Object.keys(manifest.dependencies ?? {}),
|
|
331
|
-
...Object.keys(manifest.devDependencies ?? {}),
|
|
332
|
-
];
|
|
333
|
-
return {
|
|
334
|
-
schema: schemaPath,
|
|
335
|
-
provider,
|
|
336
|
-
generator,
|
|
337
|
-
clientOutput,
|
|
338
|
-
drivers: dependencies.filter((name) => name.startsWith(DRIVER_PREFIX)),
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
function clientShape(facts) {
|
|
342
|
-
return {
|
|
343
|
-
clientClass: "PrismaClient",
|
|
344
|
-
importsClient: (imported, file) => {
|
|
345
|
-
if (!imported.startsWith(".")) {
|
|
346
|
-
return imported === "@prisma/client";
|
|
347
|
-
}
|
|
348
|
-
if (facts.clientOutput === undefined) {
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
const resolved = path.posix
|
|
352
|
-
.join(path.posix.dirname(file), imported)
|
|
353
|
-
.replace(/\.(js|ts)$/, "");
|
|
354
|
-
return (resolved === facts.clientOutput ||
|
|
355
|
-
resolved === `${facts.clientOutput}/client` ||
|
|
356
|
-
resolved === `${facts.clientOutput}/index`);
|
|
357
|
-
},
|
|
358
|
-
skip: facts.clientOutput === undefined
|
|
359
|
-
? ["src/generated"]
|
|
360
|
-
: ["src/generated", facts.clientOutput],
|
|
361
|
-
};
|
|
362
|
-
}
|
|
1
|
+
import { PRISMA7_NEST_TEMPLATES } from "./prisma7-nest.templates.js";
|
|
2
|
+
import { PRISMA7_NEXT_TEMPLATES } from "./prisma7-next.templates.js";
|
|
3
|
+
import { clientShape, PROVIDERS, readProject, } from "./prisma7-orm.templates.js";
|
|
363
4
|
export const PRISMA7_TEMPLATES = {
|
|
364
5
|
providers: PROVIDERS,
|
|
365
|
-
scaffold,
|
|
366
6
|
readProject,
|
|
367
7
|
clientShape,
|
|
368
|
-
|
|
369
|
-
|
|
8
|
+
hosts: {
|
|
9
|
+
nest: PRISMA7_NEST_TEMPLATES,
|
|
10
|
+
next: PRISMA7_NEXT_TEMPLATES,
|
|
11
|
+
},
|
|
370
12
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PRISMA7_TEMPLATES } from "./prisma7/prisma7.templates.js";
|
|
2
|
-
export const
|
|
3
|
-
|
|
2
|
+
export const SCAFFOLD_API = "/api";
|
|
3
|
+
const TEMPLATE_REGISTRY = {
|
|
4
4
|
prisma7: PRISMA7_TEMPLATES,
|
|
5
5
|
};
|
|
6
6
|
export function templatesFor(entry) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export class
|
|
3
|
-
name = "
|
|
1
|
+
import { AvWizardAnswerError } from "./wizard.questions.js";
|
|
2
|
+
export class AvUnansweredQuestionsError extends Error {
|
|
3
|
+
name = "AvUnansweredQuestionsError";
|
|
4
4
|
}
|
|
5
5
|
function flagSpelling(question, context) {
|
|
6
6
|
if (!question.flag.startsWith("--")) {
|
|
@@ -41,7 +41,7 @@ async function prompt(question, context, prompter) {
|
|
|
41
41
|
return question.parse(raw, context);
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
|
-
if (!(error instanceof
|
|
44
|
+
if (!(error instanceof AvWizardAnswerError)) {
|
|
45
45
|
throw error;
|
|
46
46
|
}
|
|
47
47
|
prompter.say(`${error.message}.`);
|
|
@@ -90,7 +90,7 @@ export async function resolveAnswers(questions, sources) {
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
if (missing.length > 0) {
|
|
93
|
-
throw new
|
|
93
|
+
throw new AvUnansweredQuestionsError(unansweredSentence(missing, sources.yes));
|
|
94
94
|
}
|
|
95
95
|
return answered;
|
|
96
96
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
-
export class
|
|
3
|
-
name = "
|
|
2
|
+
export class AvWizardCancelledError extends Error {
|
|
3
|
+
name = "AvWizardCancelledError";
|
|
4
4
|
}
|
|
5
5
|
export function createReadlinePrompter(streams) {
|
|
6
6
|
let session;
|
|
7
7
|
let ended = false;
|
|
8
8
|
const lines = [];
|
|
9
9
|
const waiting = [];
|
|
10
|
-
const cancelled = () => new
|
|
10
|
+
const cancelled = () => new AvWizardCancelledError("the wizard was closed before it was answered");
|
|
11
11
|
const open = () => {
|
|
12
12
|
if (session === undefined) {
|
|
13
13
|
session = createInterface({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { modulesProviding, } from "../project/service.detector.js";
|
|
2
2
|
export const PACKAGE_MANAGERS = ["npm", "pnpm"];
|
|
3
|
-
export class
|
|
4
|
-
name = "
|
|
3
|
+
export class AvWizardAnswerError extends Error {
|
|
4
|
+
name = "AvWizardAnswerError";
|
|
5
5
|
}
|
|
6
6
|
function ormLabel(entry) {
|
|
7
7
|
return `${entry.orm.label} ${entry.orm.major}`;
|
|
@@ -32,7 +32,7 @@ export const NAME_QUESTION = {
|
|
|
32
32
|
parse: (raw) => {
|
|
33
33
|
const name = raw.trim();
|
|
34
34
|
if (!NAME.test(name)) {
|
|
35
|
-
throw new
|
|
35
|
+
throw new AvWizardAnswerError(`${JSON.stringify(raw)} is not a project name: use letters, digits, ".", "_" and "-", starting with a letter or digit`);
|
|
36
36
|
}
|
|
37
37
|
return name;
|
|
38
38
|
},
|
|
@@ -52,7 +52,7 @@ export const ORM_QUESTION = {
|
|
|
52
52
|
const alias = raw.trim().toLowerCase();
|
|
53
53
|
const entry = context.catalog.find((candidate) => candidate.alias === alias);
|
|
54
54
|
if (entry === undefined) {
|
|
55
|
-
throw new
|
|
55
|
+
throw new AvWizardAnswerError(`there is no Aventara adapter for the ORM ${JSON.stringify(raw)}; ${ormAliasesSentence(context.catalog)}`);
|
|
56
56
|
}
|
|
57
57
|
return entry;
|
|
58
58
|
},
|
|
@@ -82,7 +82,7 @@ export const DB_QUESTION = {
|
|
|
82
82
|
const orm = requireOrm(context);
|
|
83
83
|
const db = raw.trim().toLowerCase();
|
|
84
84
|
if (!orm.providers.includes(db)) {
|
|
85
|
-
throw new
|
|
85
|
+
throw new AvWizardAnswerError(`the ${ormLabel(orm)} adapter does not support the database ${JSON.stringify(raw)}; choose ${orm.providers.join(" or ")}`);
|
|
86
86
|
}
|
|
87
87
|
return db;
|
|
88
88
|
},
|
|
@@ -98,7 +98,7 @@ export const PACKAGE_MANAGER_QUESTION = {
|
|
|
98
98
|
parse: (raw) => {
|
|
99
99
|
const manager = PACKAGE_MANAGERS.find((candidate) => candidate === raw.trim().toLowerCase());
|
|
100
100
|
if (manager === undefined) {
|
|
101
|
-
throw new
|
|
101
|
+
throw new AvWizardAnswerError(`${JSON.stringify(raw)} is not a supported package manager; choose ${PACKAGE_MANAGERS.join(" or ")}`);
|
|
102
102
|
}
|
|
103
103
|
return manager;
|
|
104
104
|
},
|
|
@@ -109,7 +109,7 @@ function reference(raw, flag) {
|
|
|
109
109
|
const exportName = raw.slice(hash + 1).trim();
|
|
110
110
|
const file = raw.slice(0, hash).trim().replace(/^\.\//, "");
|
|
111
111
|
if (hash <= 0 || !/^[A-Za-z_$][\w$]*$/.test(exportName)) {
|
|
112
|
-
throw new
|
|
112
|
+
throw new AvWizardAnswerError(`${flag} takes <path#Export>, a file and the name it exports, such as src/prisma.service.ts#PrismaService; got ${JSON.stringify(raw)}`);
|
|
113
113
|
}
|
|
114
114
|
return { file, exportName };
|
|
115
115
|
}
|
|
@@ -136,7 +136,7 @@ function serviceOptions(services) {
|
|
|
136
136
|
? found
|
|
137
137
|
: [{ ...services.newService, isNew: true }];
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
const SERVICE_QUESTION = {
|
|
140
140
|
id: "service",
|
|
141
141
|
flag: "--prisma-service",
|
|
142
142
|
placeholder: "<path#Export>",
|
|
@@ -166,7 +166,7 @@ export const SERVICE_QUESTION = {
|
|
|
166
166
|
const options = serviceOptions(services);
|
|
167
167
|
const chosen = options.find((option) => option.file === wanted.file && option.exportName === wanted.exportName);
|
|
168
168
|
if (chosen === undefined) {
|
|
169
|
-
throw new
|
|
169
|
+
throw new AvWizardAnswerError(`${spelled(wanted)} is not a service found in src/ (a class extending ${services.clientClass}, or one holding it); found: ${options
|
|
170
170
|
.filter((option) => !option.isNew)
|
|
171
171
|
.map(spelled)
|
|
172
172
|
.join(", ") || "none"}`);
|
|
@@ -175,7 +175,7 @@ export const SERVICE_QUESTION = {
|
|
|
175
175
|
},
|
|
176
176
|
describe: spelled,
|
|
177
177
|
};
|
|
178
|
-
|
|
178
|
+
const MODULE_QUESTION = {
|
|
179
179
|
id: "module",
|
|
180
180
|
flag: "--prisma-module",
|
|
181
181
|
placeholder: "<path#Export>",
|
|
@@ -202,7 +202,7 @@ export const MODULE_QUESTION = {
|
|
|
202
202
|
: modulesProviding(services.scan, service);
|
|
203
203
|
const chosen = modules.find((module) => module.file === wanted.file && module.exportName === wanted.exportName);
|
|
204
204
|
if (chosen === undefined) {
|
|
205
|
-
throw new
|
|
205
|
+
throw new AvWizardAnswerError(`${spelled(wanted)} is not a module that provides and exports ${service.exportName}; found: ${modules.map(spelled).join(", ") || "none"}`);
|
|
206
206
|
}
|
|
207
207
|
return { file: chosen.file, exportName: chosen.exportName };
|
|
208
208
|
},
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/cli",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.10",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Scaffolds an Aventara server: `aventara new` for a new NestJS project, `aventara init` for an existing one.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": "^22.
|
|
8
|
+
"node": "^22.23.4 || >=24.22.1"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"aventara": "./dist/aventara.bin.js"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"typescript": "^5.9.2"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"prebuild": "node scripts/adapter-catalog.generator.ts",
|
|
26
|
+
"prebuild": "node scripts/adapter-catalog.generator.ts && node ../../scripts/build/host-catalog.generator.ts --out src/catalog/host.catalog.generated.ts && node scripts/agent-skill.generator.ts",
|
|
27
27
|
"build": "node ../../scripts/build/build-output.cleaner.ts dist && tsc -p tsconfig.json --removeComments",
|
|
28
28
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
29
29
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|