@specverse/engines 4.1.5 → 4.1.6
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/libs/instance-factories/applications/templates/generic/backend-env-generator.js +22 -0
- package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +66 -0
- package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +54 -0
- package/dist/libs/instance-factories/applications/templates/generic/main-generator.js +290 -0
- package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +530 -0
- package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +437 -0
- package/dist/libs/instance-factories/applications/templates/react/api-types-generator.js +146 -0
- package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +73 -0
- package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +18 -0
- package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +99 -0
- package/dist/libs/instance-factories/applications/templates/react/gitignore-generator.js +35 -0
- package/dist/libs/instance-factories/applications/templates/react/index-css-generator.js +9 -0
- package/dist/libs/instance-factories/applications/templates/react/index-html-generator.js +23 -0
- package/dist/libs/instance-factories/applications/templates/react/main-tsx-generator.js +29 -0
- package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +49 -0
- package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +156 -0
- package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +935 -0
- package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/runtime-app-tsx-generator.js +101 -0
- package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +50 -0
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +646 -0
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +65 -0
- package/dist/libs/instance-factories/applications/templates/react/tsconfig-generator.js +28 -0
- package/dist/libs/instance-factories/applications/templates/react/use-api-hooks-generator.js +132 -0
- package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +143 -0
- package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +355 -0
- package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +91 -0
- package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +79 -0
- package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +42 -0
- package/dist/libs/instance-factories/cli/templates/commander/cli-bin-wrapper-generator.js +11 -0
- package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +111 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +928 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +83 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/publisher-generator.js +91 -0
- package/dist/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.js +86 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.js +93 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +280 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +125 -0
- package/dist/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.js +25 -0
- package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +371 -0
- package/dist/libs/instance-factories/orms/templates/prisma/services-generator.js +266 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/env-example-generator.js +51 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/env-generator.js +61 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.js +59 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/package-json-generator.js +126 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/readme-generator.js +159 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +56 -0
- package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.js +37 -0
- package/dist/libs/instance-factories/sdks/templates/python/sdk-generator.js +29 -0
- package/dist/libs/instance-factories/sdks/templates/typescript/sdk-generator.js +28 -0
- package/dist/libs/instance-factories/services/templates/memory/generate-interpreter.js +14 -0
- package/dist/libs/instance-factories/services/templates/memory/step-conventions-memory.js +415 -0
- package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +177 -0
- package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +413 -0
- package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +243 -0
- package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +264 -0
- package/dist/libs/instance-factories/services/templates/shared-patterns.js +24 -0
- package/dist/libs/instance-factories/shared/path-resolver.js +59 -0
- package/dist/libs/instance-factories/storage/templates/mongodb/config-generator.js +13 -0
- package/dist/libs/instance-factories/storage/templates/mongodb/docker-generator.js +16 -0
- package/dist/libs/instance-factories/storage/templates/postgresql/config-generator.js +45 -0
- package/dist/libs/instance-factories/storage/templates/postgresql/docker-generator.js +46 -0
- package/dist/libs/instance-factories/storage/templates/redis/config-generator.js +14 -0
- package/dist/libs/instance-factories/storage/templates/redis/docker-generator.js +16 -0
- package/dist/libs/instance-factories/test-generation.js +145 -0
- package/dist/libs/instance-factories/testing/templates/vitest/tests-generator.js +30 -0
- package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +149 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +232 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +49 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +18 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +97 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +64 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +182 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +1210 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +172 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +240 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +147 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +281 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +409 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +414 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +467 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +135 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
- package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +965 -0
- package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js +238 -0
- package/dist/libs/instance-factories/validation/templates/zod/validation-generator.js +25 -0
- package/dist/libs/instance-factories/views/index.js +48 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +742 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +824 -0
- package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +719 -0
- package/dist/libs/instance-factories/views/templates/react/app-generator.js +45 -0
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +779 -0
- package/dist/libs/instance-factories/views/templates/react/forms-generator.js +285 -0
- package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +46 -0
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +111 -0
- package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +9 -0
- package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +23 -0
- package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +21 -0
- package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +299 -0
- package/dist/libs/instance-factories/views/templates/react/router-generator.js +136 -0
- package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +107 -0
- package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +179 -0
- package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +7 -0
- package/dist/libs/instance-factories/views/templates/react/types-generator.js +56 -0
- package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +27 -0
- package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +29 -0
- package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +261 -0
- package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +34 -0
- package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +800 -0
- package/dist/libs/instance-factories/views/templates/shared/base-generator.js +305 -0
- package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +517 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +445 -0
- package/dist/libs/instance-factories/views/templates/shared/index.js +80 -0
- package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +210 -0
- package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +492 -0
- package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +321 -0
- package/package.json +3 -2
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
function generatePrismaService(context) {
|
|
2
|
+
const { model, controller, spec, implType } = context;
|
|
3
|
+
if (!model) {
|
|
4
|
+
throw new Error("Model is required in template context");
|
|
5
|
+
}
|
|
6
|
+
const modelName = model.name;
|
|
7
|
+
const serviceName = `${modelName}Service`;
|
|
8
|
+
const prismaModel = modelName.charAt(0).toLowerCase() + modelName.slice(1);
|
|
9
|
+
const imports = generateImports(modelName, implType);
|
|
10
|
+
const methods = generateCRUDEMethods(modelName, prismaModel, model, implType);
|
|
11
|
+
const validateMethod = generateValidationMethod(modelName, model, implType);
|
|
12
|
+
return `${imports}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ${serviceName}
|
|
16
|
+
* Generated service for ${modelName} model
|
|
17
|
+
*
|
|
18
|
+
* Provides CURED operations (Create, Retrieve, Update, Evolve, Delete)
|
|
19
|
+
* with Prisma ORM integration
|
|
20
|
+
*/
|
|
21
|
+
export class ${serviceName} {
|
|
22
|
+
constructor(private prisma: PrismaClient) {}
|
|
23
|
+
|
|
24
|
+
${methods}
|
|
25
|
+
|
|
26
|
+
${validateMethod}
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
function generateImports(modelName, implType) {
|
|
31
|
+
const imports = [
|
|
32
|
+
`import { PrismaClient, ${modelName} } from '@prisma/client';`
|
|
33
|
+
];
|
|
34
|
+
return imports.join("\n");
|
|
35
|
+
}
|
|
36
|
+
function generateCRUDEMethods(modelName, prismaModel, model, implType) {
|
|
37
|
+
const methods = [];
|
|
38
|
+
methods.push(generateCreateMethod(modelName, prismaModel, model));
|
|
39
|
+
methods.push(generateRetrieveMethod(modelName, prismaModel, model));
|
|
40
|
+
methods.push(generateUpdateMethod(modelName, prismaModel, model));
|
|
41
|
+
methods.push(generateEvolveMethod(modelName, prismaModel, model));
|
|
42
|
+
methods.push(generateDeleteMethod(modelName, prismaModel, model));
|
|
43
|
+
methods.push(generateListMethod(modelName, prismaModel, model));
|
|
44
|
+
return methods.join("\n\n");
|
|
45
|
+
}
|
|
46
|
+
function generateCreateMethod(modelName, prismaModel, model) {
|
|
47
|
+
return ` /**
|
|
48
|
+
* Create a new ${modelName}
|
|
49
|
+
*/
|
|
50
|
+
async create(data: Omit<${modelName}, 'id' | 'createdAt' | 'updatedAt'>): Promise<${modelName}> {
|
|
51
|
+
// Validate before creating
|
|
52
|
+
const validationResult = this.validate(data, { operation: 'create' });
|
|
53
|
+
if (!validationResult.valid) {
|
|
54
|
+
throw new Error(\`Validation failed: \${validationResult.errors.join(', ')}\`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return await this.prisma.${prismaModel}.create({
|
|
58
|
+
data: data as any
|
|
59
|
+
});
|
|
60
|
+
}`;
|
|
61
|
+
}
|
|
62
|
+
function generateRetrieveMethod(modelName, prismaModel, model) {
|
|
63
|
+
return ` /**
|
|
64
|
+
* Retrieve a ${modelName} by ID
|
|
65
|
+
*/
|
|
66
|
+
async retrieve(id: string): Promise<${modelName} | null> {
|
|
67
|
+
return await this.prisma.${prismaModel}.findUnique({
|
|
68
|
+
where: { id }
|
|
69
|
+
});
|
|
70
|
+
}`;
|
|
71
|
+
}
|
|
72
|
+
function generateUpdateMethod(modelName, prismaModel, model) {
|
|
73
|
+
return ` /**
|
|
74
|
+
* Update a ${modelName}
|
|
75
|
+
*/
|
|
76
|
+
async update(id: string, data: Partial<${modelName}>): Promise<${modelName}> {
|
|
77
|
+
// Validate before updating
|
|
78
|
+
const validationResult = this.validate(data, { operation: 'update' });
|
|
79
|
+
if (!validationResult.valid) {
|
|
80
|
+
throw new Error(\`Validation failed: \${validationResult.errors.join(', ')}\`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return await this.prisma.${prismaModel}.update({
|
|
84
|
+
where: { id },
|
|
85
|
+
data: data as any
|
|
86
|
+
});
|
|
87
|
+
}`;
|
|
88
|
+
}
|
|
89
|
+
function generateEvolveMethod(modelName, prismaModel, model) {
|
|
90
|
+
const hasLifecycle = model.lifecycle && model.lifecycle.length > 0;
|
|
91
|
+
if (hasLifecycle) {
|
|
92
|
+
return ` /**
|
|
93
|
+
* Evolve a ${modelName} (lifecycle-aware update)
|
|
94
|
+
* Handles state transitions according to lifecycle rules
|
|
95
|
+
*/
|
|
96
|
+
async evolve(id: string, data: Partial<${modelName}>): Promise<${modelName}> {
|
|
97
|
+
// Get current instance to check state
|
|
98
|
+
const current = await this.retrieve(id);
|
|
99
|
+
if (!current) {
|
|
100
|
+
throw new Error('${modelName} not found');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Validate state transition if status is being changed
|
|
104
|
+
if (data.status && data.status !== current.status) {
|
|
105
|
+
// TODO: Add lifecycle validation based on model.lifecycle
|
|
106
|
+
// For now, allow any transition
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Validate data
|
|
110
|
+
const validationResult = this.validate(data, { operation: 'evolve' });
|
|
111
|
+
if (!validationResult.valid) {
|
|
112
|
+
throw new Error(\`Validation failed: \${validationResult.errors.join(', ')}\`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return await this.prisma.${prismaModel}.update({
|
|
116
|
+
where: { id },
|
|
117
|
+
data: data as any
|
|
118
|
+
});
|
|
119
|
+
}`;
|
|
120
|
+
} else {
|
|
121
|
+
return ` /**
|
|
122
|
+
* Evolve a ${modelName} (same as update for models without lifecycle)
|
|
123
|
+
*/
|
|
124
|
+
async evolve(id: string, data: Partial<${modelName}>): Promise<${modelName}> {
|
|
125
|
+
return this.update(id, data);
|
|
126
|
+
}`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function generateDeleteMethod(modelName, prismaModel, model) {
|
|
130
|
+
const hasSoftDelete = model.metadata?.softDelete;
|
|
131
|
+
if (hasSoftDelete) {
|
|
132
|
+
return ` /**
|
|
133
|
+
* Delete a ${modelName} (soft delete)
|
|
134
|
+
*/
|
|
135
|
+
async delete(id: string): Promise<void> {
|
|
136
|
+
await this.prisma.${prismaModel}.update({
|
|
137
|
+
where: { id },
|
|
138
|
+
data: {
|
|
139
|
+
deletedAt: new Date(),
|
|
140
|
+
isDeleted: true
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}`;
|
|
144
|
+
} else {
|
|
145
|
+
return ` /**
|
|
146
|
+
* Delete a ${modelName}
|
|
147
|
+
*/
|
|
148
|
+
async delete(id: string): Promise<void> {
|
|
149
|
+
await this.prisma.${prismaModel}.delete({
|
|
150
|
+
where: { id }
|
|
151
|
+
});
|
|
152
|
+
}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function generateListMethod(modelName, prismaModel, model) {
|
|
156
|
+
const hasSoftDelete = model.metadata?.softDelete;
|
|
157
|
+
const whereClause = hasSoftDelete ? `
|
|
158
|
+
where: { isDeleted: false },` : "";
|
|
159
|
+
return ` /**
|
|
160
|
+
* List all ${modelName}s
|
|
161
|
+
*/
|
|
162
|
+
async list(): Promise<${modelName}[]> {
|
|
163
|
+
return await this.prisma.${prismaModel}.findMany({${whereClause}
|
|
164
|
+
orderBy: { createdAt: 'desc' }
|
|
165
|
+
});
|
|
166
|
+
}`;
|
|
167
|
+
}
|
|
168
|
+
function generateValidationMethod(modelName, model, implType) {
|
|
169
|
+
return ` /**
|
|
170
|
+
* Validate ${modelName} data
|
|
171
|
+
* Unified validation method for all operations
|
|
172
|
+
*/
|
|
173
|
+
validate(
|
|
174
|
+
data: any,
|
|
175
|
+
context: { operation: 'create' | 'update' | 'evolve' }
|
|
176
|
+
): { valid: boolean; errors: string[] } {
|
|
177
|
+
const errors: string[] = [];
|
|
178
|
+
|
|
179
|
+
// Required field validation (for create operation)
|
|
180
|
+
if (context.operation === 'create') {
|
|
181
|
+
${generateRequiredFieldValidation(model)}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Type validation
|
|
185
|
+
${generateTypeValidation(model)}
|
|
186
|
+
|
|
187
|
+
// Constraint validation
|
|
188
|
+
${generateConstraintValidation(model)}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
valid: errors.length === 0,
|
|
192
|
+
errors
|
|
193
|
+
};
|
|
194
|
+
}`;
|
|
195
|
+
}
|
|
196
|
+
function generateRequiredFieldValidation(model) {
|
|
197
|
+
const requiredFields = model.attributes?.filter(
|
|
198
|
+
(attr) => attr.constraints?.required && !["id", "createdAt", "updatedAt"].includes(attr.name)
|
|
199
|
+
) || [];
|
|
200
|
+
if (requiredFields.length === 0) {
|
|
201
|
+
return "// No required fields";
|
|
202
|
+
}
|
|
203
|
+
const validations = requiredFields.map(
|
|
204
|
+
(field) => `if (data.${field.name} === undefined || data.${field.name} === null) {
|
|
205
|
+
errors.push('${field.name} is required');
|
|
206
|
+
}`
|
|
207
|
+
).join("\n ");
|
|
208
|
+
return validations;
|
|
209
|
+
}
|
|
210
|
+
function generateTypeValidation(model) {
|
|
211
|
+
const fields = model.attributes || [];
|
|
212
|
+
if (fields.length === 0) {
|
|
213
|
+
return "// No type validation";
|
|
214
|
+
}
|
|
215
|
+
const validations = fields.filter((field) => !["id", "createdAt", "updatedAt"].includes(field.name)).map((field) => {
|
|
216
|
+
const typeName = inferJsType(field.type);
|
|
217
|
+
if (typeName === "string" || typeName === "number" || typeName === "boolean") {
|
|
218
|
+
return `if (data.${field.name} !== undefined && typeof data.${field.name} !== '${typeName}') {
|
|
219
|
+
errors.push('${field.name} must be a ${typeName}');
|
|
220
|
+
}`;
|
|
221
|
+
}
|
|
222
|
+
return "";
|
|
223
|
+
}).filter((v) => v).join("\n ");
|
|
224
|
+
return validations || "// No type validation needed";
|
|
225
|
+
}
|
|
226
|
+
function generateConstraintValidation(model) {
|
|
227
|
+
const fields = model.attributes?.filter(
|
|
228
|
+
(attr) => attr.constraints && Object.keys(attr.constraints).length > 0
|
|
229
|
+
) || [];
|
|
230
|
+
if (fields.length === 0) {
|
|
231
|
+
return "// No constraint validation";
|
|
232
|
+
}
|
|
233
|
+
const validations = fields.map((field) => {
|
|
234
|
+
const constraints = [];
|
|
235
|
+
if (field.constraints.unique) {
|
|
236
|
+
constraints.push(`// TODO: Check uniqueness of ${field.name}`);
|
|
237
|
+
}
|
|
238
|
+
if (field.constraints.min !== void 0) {
|
|
239
|
+
constraints.push(
|
|
240
|
+
`if (data.${field.name} !== undefined && data.${field.name} < ${field.constraints.min}) {
|
|
241
|
+
errors.push('${field.name} must be at least ${field.constraints.min}');
|
|
242
|
+
}`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (field.constraints.max !== void 0) {
|
|
246
|
+
constraints.push(
|
|
247
|
+
`if (data.${field.name} !== undefined && data.${field.name} > ${field.constraints.max}) {
|
|
248
|
+
errors.push('${field.name} must be at most ${field.constraints.max}');
|
|
249
|
+
}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return constraints.join("\n ");
|
|
253
|
+
}).filter((v) => v).join("\n ");
|
|
254
|
+
return validations || "// No constraint validation needed";
|
|
255
|
+
}
|
|
256
|
+
function inferJsType(type) {
|
|
257
|
+
const typeLower = type.toLowerCase();
|
|
258
|
+
if (typeLower.includes("string") || typeLower.includes("text")) return "string";
|
|
259
|
+
if (typeLower.includes("int") || typeLower.includes("number")) return "number";
|
|
260
|
+
if (typeLower.includes("bool")) return "boolean";
|
|
261
|
+
if (typeLower.includes("date")) return "object";
|
|
262
|
+
return "any";
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
generatePrismaService as default
|
|
266
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function generateEnvExample(context) {
|
|
2
|
+
const { manifest } = context;
|
|
3
|
+
const envVars = aggregateEnvironmentVariables(context.implementationTypes || []);
|
|
4
|
+
const byCategory = {};
|
|
5
|
+
for (const envVar of envVars) {
|
|
6
|
+
const category = envVar.category || "Configuration";
|
|
7
|
+
if (!byCategory[category]) {
|
|
8
|
+
byCategory[category] = [];
|
|
9
|
+
}
|
|
10
|
+
byCategory[category].push(envVar);
|
|
11
|
+
}
|
|
12
|
+
const lines = [
|
|
13
|
+
"# Environment Configuration",
|
|
14
|
+
"# Copy this file to .env and fill in your values",
|
|
15
|
+
""
|
|
16
|
+
];
|
|
17
|
+
for (const [category, vars] of Object.entries(byCategory)) {
|
|
18
|
+
lines.push(`# ${category}`);
|
|
19
|
+
for (const envVar of vars) {
|
|
20
|
+
if (envVar.description) {
|
|
21
|
+
lines.push(`# ${envVar.description}`);
|
|
22
|
+
}
|
|
23
|
+
if (envVar.required) {
|
|
24
|
+
lines.push(`# REQUIRED`);
|
|
25
|
+
}
|
|
26
|
+
const value = envVar.example || "";
|
|
27
|
+
lines.push(`${envVar.name}="${value}"`);
|
|
28
|
+
}
|
|
29
|
+
lines.push("");
|
|
30
|
+
}
|
|
31
|
+
return lines.join("\n");
|
|
32
|
+
}
|
|
33
|
+
function aggregateEnvironmentVariables(implementationTypes) {
|
|
34
|
+
const envVarsMap = /* @__PURE__ */ new Map();
|
|
35
|
+
if (!implementationTypes || implementationTypes.length === 0) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
for (const implType of implementationTypes) {
|
|
39
|
+
if (implType.requirements?.environment) {
|
|
40
|
+
for (const envVar of implType.requirements.environment) {
|
|
41
|
+
if (!envVarsMap.has(envVar.name)) {
|
|
42
|
+
envVarsMap.set(envVar.name, envVar);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return Array.from(envVarsMap.values());
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
generateEnvExample as default
|
|
51
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
function generateEnv(context) {
|
|
4
|
+
const { manifest } = context;
|
|
5
|
+
const envVars = aggregateEnvironmentVariables(context.implementationTypes || []);
|
|
6
|
+
let parentDatabaseUrl;
|
|
7
|
+
try {
|
|
8
|
+
const parentEnvPath = join(process.cwd(), ".env");
|
|
9
|
+
if (existsSync(parentEnvPath)) {
|
|
10
|
+
const parentEnvContent = readFileSync(parentEnvPath, "utf-8");
|
|
11
|
+
const match = parentEnvContent.match(/DATABASE_URL=["']?([^"'\n]+)["']?/);
|
|
12
|
+
if (match) {
|
|
13
|
+
parentDatabaseUrl = match[1];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
} catch (err) {
|
|
17
|
+
}
|
|
18
|
+
const byCategory = {};
|
|
19
|
+
for (const envVar of envVars) {
|
|
20
|
+
const category = envVar.category || "Configuration";
|
|
21
|
+
if (!byCategory[category]) {
|
|
22
|
+
byCategory[category] = [];
|
|
23
|
+
}
|
|
24
|
+
byCategory[category].push(envVar);
|
|
25
|
+
}
|
|
26
|
+
const lines = [];
|
|
27
|
+
for (const [category, vars] of Object.entries(byCategory)) {
|
|
28
|
+
lines.push(`# ${category}`);
|
|
29
|
+
for (const envVar of vars) {
|
|
30
|
+
if (envVar.description) {
|
|
31
|
+
lines.push(`# ${envVar.description}`);
|
|
32
|
+
}
|
|
33
|
+
let value = envVar.example || "";
|
|
34
|
+
if (envVar.name === "DATABASE_URL" && parentDatabaseUrl) {
|
|
35
|
+
value = parentDatabaseUrl;
|
|
36
|
+
}
|
|
37
|
+
lines.push(`${envVar.name}="${value}"`);
|
|
38
|
+
}
|
|
39
|
+
lines.push("");
|
|
40
|
+
}
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
function aggregateEnvironmentVariables(implementationTypes) {
|
|
44
|
+
const envVarsMap = /* @__PURE__ */ new Map();
|
|
45
|
+
if (!implementationTypes || implementationTypes.length === 0) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
for (const implType of implementationTypes) {
|
|
49
|
+
if (implType.requirements?.environment) {
|
|
50
|
+
for (const envVar of implType.requirements.environment) {
|
|
51
|
+
if (!envVarsMap.has(envVar.name)) {
|
|
52
|
+
envVarsMap.set(envVar.name, envVar);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return Array.from(envVarsMap.values());
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
generateEnv as default
|
|
61
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function generateGitIgnore(context) {
|
|
2
|
+
const { manifest } = context;
|
|
3
|
+
const ignorePatterns = [
|
|
4
|
+
"# Dependencies",
|
|
5
|
+
"node_modules/",
|
|
6
|
+
"",
|
|
7
|
+
"# Build output",
|
|
8
|
+
"dist/",
|
|
9
|
+
"build/",
|
|
10
|
+
"",
|
|
11
|
+
"# Environment",
|
|
12
|
+
".env",
|
|
13
|
+
".env.local",
|
|
14
|
+
".env.*.local",
|
|
15
|
+
"",
|
|
16
|
+
"# TypeScript",
|
|
17
|
+
"*.tsbuildinfo",
|
|
18
|
+
"",
|
|
19
|
+
"# Logs",
|
|
20
|
+
"logs/",
|
|
21
|
+
"*.log",
|
|
22
|
+
"npm-debug.log*",
|
|
23
|
+
""
|
|
24
|
+
];
|
|
25
|
+
if (usesPrisma(manifest)) {
|
|
26
|
+
ignorePatterns.push(
|
|
27
|
+
"# Prisma",
|
|
28
|
+
"prisma/migrations/",
|
|
29
|
+
""
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
ignorePatterns.push(
|
|
33
|
+
"# IDE",
|
|
34
|
+
".vscode/",
|
|
35
|
+
".idea/",
|
|
36
|
+
"*.swp",
|
|
37
|
+
"*.swo",
|
|
38
|
+
"",
|
|
39
|
+
"# OS",
|
|
40
|
+
".DS_Store",
|
|
41
|
+
"Thumbs.db",
|
|
42
|
+
"",
|
|
43
|
+
"# Testing",
|
|
44
|
+
"coverage/",
|
|
45
|
+
".nyc_output/"
|
|
46
|
+
);
|
|
47
|
+
return ignorePatterns.join("\n");
|
|
48
|
+
}
|
|
49
|
+
function usesPrisma(manifest) {
|
|
50
|
+
if (!manifest || !manifest.capabilityMappings) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return manifest.capabilityMappings.some(
|
|
54
|
+
(m) => m.instanceFactory?.toLowerCase().includes("prisma")
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
generateGitIgnore as default
|
|
59
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
function generatePackageJson(context) {
|
|
2
|
+
const { spec, manifest, configuration } = context;
|
|
3
|
+
const outputStructure = configuration?.outputStructure || "monorepo";
|
|
4
|
+
const isMonorepo = outputStructure === "monorepo";
|
|
5
|
+
const aggregated = aggregateRequirements(context.instanceFactories || context.implementationTypes || []);
|
|
6
|
+
const pkg = {
|
|
7
|
+
name: (spec.metadata?.component || "specverse-app").toLowerCase().replace(/\s+/g, "-"),
|
|
8
|
+
version: spec.metadata?.version || "1.0.0",
|
|
9
|
+
description: spec.metadata?.description || "",
|
|
10
|
+
private: true,
|
|
11
|
+
type: "module"
|
|
12
|
+
};
|
|
13
|
+
if (isMonorepo) {
|
|
14
|
+
const frontendDir = configuration?.frontendDir || "frontend";
|
|
15
|
+
const backendDir = configuration?.backendDir || "backend";
|
|
16
|
+
pkg.workspaces = [backendDir, frontendDir];
|
|
17
|
+
pkg.scripts = {
|
|
18
|
+
// Build all workspaces
|
|
19
|
+
"build": "npm run build --workspaces",
|
|
20
|
+
"build:backend": `npm run build --workspace=${backendDir}`,
|
|
21
|
+
"build:frontend": `npm run build --workspace=${frontendDir}`,
|
|
22
|
+
// Production
|
|
23
|
+
"start": `npm run start --workspace=${backendDir}`,
|
|
24
|
+
// Development
|
|
25
|
+
"dev": "npm run dev --workspaces",
|
|
26
|
+
"dev:backend": `npm run dev --workspace=${backendDir}`,
|
|
27
|
+
"dev:frontend": `npm run dev --workspace=${frontendDir}`,
|
|
28
|
+
// Testing
|
|
29
|
+
"test": "npm test --workspaces",
|
|
30
|
+
"test:backend": `npm test --workspace=${backendDir}`,
|
|
31
|
+
"test:frontend": `npm test --workspace=${frontendDir}`,
|
|
32
|
+
// Database management (backend)
|
|
33
|
+
"db:setup": `npm run db:setup --workspace=${backendDir}`,
|
|
34
|
+
"db:generate": `npm run db:generate --workspace=${backendDir}`,
|
|
35
|
+
"db:push": `npm run db:push --workspace=${backendDir}`,
|
|
36
|
+
"db:migrate": `npm run db:migrate --workspace=${backendDir}`,
|
|
37
|
+
"db:studio": `npm run db:studio --workspace=${backendDir}`,
|
|
38
|
+
// Linting
|
|
39
|
+
"lint": "npm run lint --workspaces",
|
|
40
|
+
"lint:fix": "npm run lint:fix --workspaces"
|
|
41
|
+
};
|
|
42
|
+
} else {
|
|
43
|
+
pkg.scripts = {
|
|
44
|
+
...aggregated.scripts,
|
|
45
|
+
// Ensure we have build script if TypeScript
|
|
46
|
+
...aggregated.usesTypeScript && !aggregated.scripts.build && {
|
|
47
|
+
build: "tsc"
|
|
48
|
+
},
|
|
49
|
+
// Add database setup script
|
|
50
|
+
"db:setup": `node -e "const{execSync:e}=require('child_process');const{readFileSync:r}=require('fs');try{const d=r('.env','utf8').match(/DATABASE_URL=.*\\/([^?\\"]+)/)?.[1];if(!d)throw new Error('DATABASE_URL not found');console.log('Creating database:',d);try{e('createdb '+d)}catch(err){const errMsg=err.stderr?err.stderr.toString():err.message;if(errMsg.includes('already exists')){console.log('\u2705 Database already exists')}else throw err}console.log('\u2705 Database created successfully')}catch(err){console.error('\u274C Database setup failed:',err.message);process.exit(1)}"`,
|
|
51
|
+
// Add test script if not provided by implementation types
|
|
52
|
+
...!aggregated.scripts.test && {
|
|
53
|
+
test: 'echo "Error: no test specified" && exit 1'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
pkg.dependencies = aggregated.dependencies;
|
|
57
|
+
pkg.devDependencies = {
|
|
58
|
+
...aggregated.devDependencies,
|
|
59
|
+
// Ensure TypeScript is present if needed
|
|
60
|
+
...aggregated.usesTypeScript && !aggregated.devDependencies.typescript && {
|
|
61
|
+
typescript: "^5.3.0",
|
|
62
|
+
"@types/node": "^20.0.0"
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
if (aggregated.usesTypeScript) {
|
|
66
|
+
pkg.main = "dist/main.js";
|
|
67
|
+
pkg.types = "dist/main.d.ts";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
pkg.engines = {
|
|
71
|
+
node: ">=18.0.0"
|
|
72
|
+
};
|
|
73
|
+
return JSON.stringify(pkg, null, 2);
|
|
74
|
+
}
|
|
75
|
+
function aggregateRequirements(implementationTypes) {
|
|
76
|
+
const result = {
|
|
77
|
+
dependencies: {},
|
|
78
|
+
devDependencies: {},
|
|
79
|
+
scripts: {},
|
|
80
|
+
environment: [],
|
|
81
|
+
usesTypeScript: false,
|
|
82
|
+
usesDocker: false
|
|
83
|
+
};
|
|
84
|
+
if (!implementationTypes || implementationTypes.length === 0) {
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
for (const implType of implementationTypes) {
|
|
88
|
+
if (implType.dependencies) {
|
|
89
|
+
if (implType.dependencies.runtime && Array.isArray(implType.dependencies.runtime)) {
|
|
90
|
+
for (const dep of implType.dependencies.runtime) {
|
|
91
|
+
if (dep.name && dep.version) {
|
|
92
|
+
result.dependencies[dep.name] = dep.version;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (implType.dependencies.dev && Array.isArray(implType.dependencies.dev)) {
|
|
97
|
+
for (const dep of implType.dependencies.dev) {
|
|
98
|
+
if (dep.name && dep.version) {
|
|
99
|
+
result.devDependencies[dep.name] = dep.version;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const requirements = implType.requirements;
|
|
105
|
+
if (requirements) {
|
|
106
|
+
if (requirements.dependencies?.npm) {
|
|
107
|
+
Object.assign(result.dependencies, requirements.dependencies.npm.dependencies || {});
|
|
108
|
+
Object.assign(result.devDependencies, requirements.dependencies.npm.devDependencies || {});
|
|
109
|
+
Object.assign(result.scripts, requirements.dependencies.npm.scripts || {});
|
|
110
|
+
}
|
|
111
|
+
if (requirements.environment) {
|
|
112
|
+
result.environment.push(...requirements.environment);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (implType.technology?.language === "typescript") {
|
|
116
|
+
result.usesTypeScript = true;
|
|
117
|
+
}
|
|
118
|
+
if (implType.capabilities?.provides?.includes("container.dev")) {
|
|
119
|
+
result.usesDocker = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
generatePackageJson as default
|
|
126
|
+
};
|