@specverse/engines 4.1.18 → 4.1.20
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/react/package-json-generator.js +1 -1
- package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +1 -1
- package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +15 -5
- package/dist/libs/instance-factories/services/templates/fastify/service-routes-generator.js +87 -0
- package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js +7 -1
- package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js.bak +244 -0
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +21 -0
- package/dist/realize/index.js.bak +758 -0
- package/dist/realize/index.js.map +1 -1
- package/libs/instance-factories/applications/templates/react/package-json-generator.ts +1 -1
- package/libs/instance-factories/applications/templates/react/runtime-package-json-generator.ts +1 -1
- package/libs/instance-factories/controllers/fastify.yaml +5 -0
- package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +16 -5
- package/libs/instance-factories/services/templates/fastify/service-routes-generator.ts +142 -0
- package/libs/instance-factories/tools/templates/mcp/static/package.json +2 -2
- package/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.ts +7 -1
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@ function generatePackageJson(context) {
|
|
|
34
34
|
"postcss": "^8.4.47",
|
|
35
35
|
"tailwindcss": "^3.4.13",
|
|
36
36
|
"typescript": "^5.2.0",
|
|
37
|
-
"vite": "^
|
|
37
|
+
"vite": "^6.0.0",
|
|
38
38
|
"eslint": "^8.53.0",
|
|
39
39
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
|
40
40
|
"@typescript-eslint/parser": "^6.10.0",
|
package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js
CHANGED
|
@@ -36,7 +36,7 @@ function generateRuntimePackageJson(context) {
|
|
|
36
36
|
"postcss": "^8.4.47",
|
|
37
37
|
"tailwindcss": "^3.4.13",
|
|
38
38
|
"typescript": "^5.2.0",
|
|
39
|
-
"vite": "^
|
|
39
|
+
"vite": "^6.0.0",
|
|
40
40
|
"eslint": "^8.53.0",
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
|
42
42
|
"@typescript-eslint/parser": "^6.10.0",
|
|
@@ -13,9 +13,17 @@ function generateFastifyServer(context) {
|
|
|
13
13
|
const specEvents = spec.events ? Object.keys(spec.events) : [];
|
|
14
14
|
const hasEvents = specEvents.length > 0 || modelNames.length > 0;
|
|
15
15
|
const servicesList = spec.services ? Object.keys(spec.services) : [];
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const serviceSingletonImports = servicesList.map((name) => {
|
|
17
|
+
const lower = name.charAt(0).toLowerCase() + name.slice(1);
|
|
18
|
+
return `import ${lower} from './services/${name}.js';`;
|
|
19
|
+
}).join("\n");
|
|
20
|
+
const serviceRouteImports = servicesList.map(
|
|
21
|
+
(name) => `import ${name}Routes from './routes/${name}Routes.js';`
|
|
22
|
+
).join("\n");
|
|
23
|
+
const serviceRouteRegistrations = servicesList.map((name) => {
|
|
24
|
+
const lower = name.charAt(0).toLowerCase() + name.slice(1);
|
|
25
|
+
return ` await fastify.register(${name}Routes, { prefix: '/api/services/${name}', services: { ${name}: ${lower} } });`;
|
|
26
|
+
}).join("\n");
|
|
19
27
|
return `/**
|
|
20
28
|
* Fastify Server
|
|
21
29
|
* Generated from SpecVerse specification
|
|
@@ -60,11 +68,13 @@ fastify.get('/api/events', async () => eventBus.getHistory());` : ""}
|
|
|
60
68
|
// Register routes
|
|
61
69
|
${routeImports}
|
|
62
70
|
|
|
63
|
-
${
|
|
64
|
-
${
|
|
71
|
+
${serviceSingletonImports ? `// Service singletons + their Fastify route files
|
|
72
|
+
${serviceSingletonImports}
|
|
73
|
+
${serviceRouteImports}` : ""}
|
|
65
74
|
|
|
66
75
|
async function registerRoutes() {
|
|
67
76
|
${routeRegistrations}
|
|
77
|
+
${serviceRouteRegistrations ? "\n // Service operation routes (RPC-style under /api/services/{ServiceName})\n" + serviceRouteRegistrations : ""}
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
// Start server
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
function generateFastifyServiceRoutes(context) {
|
|
2
|
+
const { service } = context;
|
|
3
|
+
if (!service) {
|
|
4
|
+
throw new Error("Service is required in template context");
|
|
5
|
+
}
|
|
6
|
+
const serviceName = service.name;
|
|
7
|
+
if (!serviceName) {
|
|
8
|
+
throw new Error("Service must have a name");
|
|
9
|
+
}
|
|
10
|
+
const operationEntries = Array.isArray(service.operations) ? service.operations.map((op) => [op.name, op]) : Object.entries(service.operations || {});
|
|
11
|
+
if (operationEntries.length === 0) {
|
|
12
|
+
return `/**
|
|
13
|
+
* ${serviceName} \u2014 no operations declared, no routes generated.
|
|
14
|
+
*/
|
|
15
|
+
import type { FastifyInstance } from 'fastify';
|
|
16
|
+
|
|
17
|
+
export default async function ${serviceName}Routes(_fastify: FastifyInstance, _options: any) {
|
|
18
|
+
// No operations declared on ${serviceName}.
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
const routeHandlers = operationEntries.map(([opName, op]) => generateOperationRoute(opName, op, serviceName)).join("\n\n");
|
|
23
|
+
return `import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ${serviceName} Routes
|
|
27
|
+
*
|
|
28
|
+
* Generated from SpecVerse specification \u2014 exposes service operations
|
|
29
|
+
* as HTTP endpoints under /api/services/${serviceName}/.
|
|
30
|
+
*
|
|
31
|
+
* Operations: ${operationEntries.map(([n]) => n).join(", ")}
|
|
32
|
+
*/
|
|
33
|
+
export default async function ${serviceName}Routes(
|
|
34
|
+
fastify: FastifyInstance,
|
|
35
|
+
options: any
|
|
36
|
+
) {
|
|
37
|
+
const service = options.services.${serviceName};
|
|
38
|
+
if (!service) {
|
|
39
|
+
throw new Error('Service ${serviceName} not registered in options.services');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
${routeHandlers.split("\n").map((line) => " " + line).join("\n")}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
function generateOperationRoute(opName, op, serviceName) {
|
|
47
|
+
const params = op?.parameters || {};
|
|
48
|
+
const paramNames = Object.keys(params);
|
|
49
|
+
const validations = [];
|
|
50
|
+
for (const [pName, pDef] of Object.entries(params)) {
|
|
51
|
+
const required = typeof pDef === "string" ? pDef.includes("required") : pDef?.required;
|
|
52
|
+
const typeStr = typeof pDef === "string" ? pDef.split(" ")[0] : pDef?.type || "String";
|
|
53
|
+
if (required) {
|
|
54
|
+
validations.push(` if (body.${pName} === undefined || body.${pName} === null) errors.push({ field: '${pName}', message: '${pName} is required' });`);
|
|
55
|
+
}
|
|
56
|
+
if (typeStr === "UUID" || typeStr === "String" || typeStr === "Email") {
|
|
57
|
+
validations.push(` if (body.${pName} !== undefined && typeof body.${pName} !== 'string') errors.push({ field: '${pName}', message: '${pName} must be a string' });`);
|
|
58
|
+
} else if (typeStr === "Integer" || typeStr === "Number" || typeStr === "Float") {
|
|
59
|
+
validations.push(` if (body.${pName} !== undefined && typeof body.${pName} !== 'number') errors.push({ field: '${pName}', message: '${pName} must be a number' });`);
|
|
60
|
+
} else if (typeStr === "Boolean") {
|
|
61
|
+
validations.push(` if (body.${pName} !== undefined && typeof body.${pName} !== 'boolean') errors.push({ field: '${pName}', message: '${pName} must be a boolean' });`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const validationBlock = validations.length > 0 ? ` const errors: Array<{ field: string; message: string }> = [];
|
|
65
|
+
${validations.join("\n")}
|
|
66
|
+
if (errors.length > 0) return reply.status(400).send({ error: 'Validation failed', details: errors });` : "";
|
|
67
|
+
const callExpression = paramNames.length > 0 ? `service.${opName}(${paramNames.map((p) => `body.${p}`).join(", ")})` : `service.${opName}(body)`;
|
|
68
|
+
return `// ${opName}${op?.description ? ` \u2014 ${op.description}` : ""}
|
|
69
|
+
fastify.post('/${opName}', {
|
|
70
|
+
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
|
71
|
+
try {
|
|
72
|
+
const body = (request.body || {}) as Record<string, any>;
|
|
73
|
+
${validationBlock}
|
|
74
|
+
const result = await ${callExpression};
|
|
75
|
+
return reply.send(result ?? { success: true });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return reply.status(400).send({
|
|
78
|
+
error: 'Failed to execute ${serviceName}.${opName}',
|
|
79
|
+
message: error instanceof Error ? error.message : String(error)
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});`;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
generateFastifyServiceRoutes as default
|
|
87
|
+
};
|
|
@@ -195,7 +195,13 @@ function generatePackageJson(commands, _entityTypes, specVersion = "1.0.0") {
|
|
|
195
195
|
devDependencies: {
|
|
196
196
|
"@types/vscode": "^1.80.0",
|
|
197
197
|
"@vscode/vsce": "^3.0.0",
|
|
198
|
-
esbuild: "^0.
|
|
198
|
+
esbuild: "^0.25.0"
|
|
199
|
+
},
|
|
200
|
+
overrides: {
|
|
201
|
+
// Force the patched version of lodash through @vscode/vsce →
|
|
202
|
+
// @secretlint → @textlint transitive chain (4.17.23 has a
|
|
203
|
+
// code-injection advisory in `_.template`).
|
|
204
|
+
lodash: "^4.18.1"
|
|
199
205
|
}
|
|
200
206
|
};
|
|
201
207
|
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync, mkdirSync, writeFileSync, copyFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __generatorDir = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
function generateVSCodeExtension(context) {
|
|
6
|
+
const { spec, outputDir } = context;
|
|
7
|
+
const extensionDir = join(outputDir || ".", "tools", "vscode-extension");
|
|
8
|
+
if (!existsSync(extensionDir)) mkdirSync(extensionDir, { recursive: true });
|
|
9
|
+
const distribution = extractDistribution(spec);
|
|
10
|
+
let cliCommands = [];
|
|
11
|
+
if (distribution?.commands) {
|
|
12
|
+
cliCommands = distribution.commands.map((cmd) => {
|
|
13
|
+
const parts = (cmd.from || cmd.command || "").split(".");
|
|
14
|
+
const name = parts[parts.length - 1] || "unknown";
|
|
15
|
+
return {
|
|
16
|
+
command: `specverse.${name}`,
|
|
17
|
+
title: cmd.title || `${capitalize(name)}: ${cmd.description || ""}`,
|
|
18
|
+
category: "SpecVerse"
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (cliCommands.length === 0) {
|
|
23
|
+
cliCommands = extractCLICommands(spec);
|
|
24
|
+
}
|
|
25
|
+
if (cliCommands.length === 0) {
|
|
26
|
+
cliCommands = getStandardCommands();
|
|
27
|
+
}
|
|
28
|
+
const entityTypes = extractEntityTypes(spec);
|
|
29
|
+
const specVersion = distribution?.version || spec?.metadata?.version || spec?.version || "4.0.0";
|
|
30
|
+
const packageJson = generatePackageJson(cliCommands, entityTypes, specVersion);
|
|
31
|
+
if (distribution) {
|
|
32
|
+
if (distribution.displayName) packageJson.displayName = distribution.displayName;
|
|
33
|
+
if (distribution.publisher) packageJson.publisher = distribution.publisher;
|
|
34
|
+
if (distribution.description) packageJson.description = distribution.description;
|
|
35
|
+
if (distribution.languages && distribution.languages.length > 0) {
|
|
36
|
+
packageJson.contributes.languages = distribution.languages.map((lang) => ({
|
|
37
|
+
id: lang.id,
|
|
38
|
+
aliases: lang.aliases || [lang.id],
|
|
39
|
+
extensions: lang.extensions || [],
|
|
40
|
+
configuration: `./language-configuration.json`
|
|
41
|
+
}));
|
|
42
|
+
packageJson.contributes.grammars = distribution.languages.filter((lang) => lang.grammar).map((lang) => ({
|
|
43
|
+
language: lang.id,
|
|
44
|
+
scopeName: lang.grammar,
|
|
45
|
+
path: `./syntaxes/${lang.id}.tmLanguage.json`
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
if (distribution.themes && distribution.themes.length > 0) {
|
|
49
|
+
packageJson.contributes.themes = distribution.themes.map((theme) => ({
|
|
50
|
+
label: theme.name,
|
|
51
|
+
uiTheme: theme.type === "dark" ? "vs-dark" : theme.type === "light" ? "vs" : "hc-black",
|
|
52
|
+
path: `./themes/${theme.name.toLowerCase().replace(/\s+/g, "-")}-theme.json`
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
writeFileSync(join(extensionDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
57
|
+
let staticDir = join(__generatorDir, "static");
|
|
58
|
+
if (!existsSync(staticDir)) {
|
|
59
|
+
staticDir = join(__generatorDir.replace("/dist/libs/", "/libs/"), "static");
|
|
60
|
+
}
|
|
61
|
+
if (existsSync(staticDir)) {
|
|
62
|
+
copyRecursive(staticDir, extensionDir);
|
|
63
|
+
}
|
|
64
|
+
const srcDir = join(extensionDir, "src");
|
|
65
|
+
if (!existsSync(srcDir)) mkdirSync(srcDir, { recursive: true });
|
|
66
|
+
const extTs = join(staticDir, "extension.ts");
|
|
67
|
+
if (existsSync(extTs)) {
|
|
68
|
+
copyFileSync(extTs, join(srcDir, "extension.ts"));
|
|
69
|
+
}
|
|
70
|
+
const buildScript = `const esbuild = require('esbuild');
|
|
71
|
+
esbuild.build({
|
|
72
|
+
entryPoints: ['src/extension.ts'],
|
|
73
|
+
bundle: true,
|
|
74
|
+
outfile: 'dist/extension.js',
|
|
75
|
+
external: ['vscode'],
|
|
76
|
+
format: 'cjs',
|
|
77
|
+
platform: 'node',
|
|
78
|
+
}).catch(() => process.exit(1));
|
|
79
|
+
`;
|
|
80
|
+
const scriptsDir = join(extensionDir, "scripts");
|
|
81
|
+
if (!existsSync(scriptsDir)) mkdirSync(scriptsDir, { recursive: true });
|
|
82
|
+
writeFileSync(join(scriptsDir, "build.js"), buildScript);
|
|
83
|
+
return `VSCode extension generated in: ${extensionDir}
|
|
84
|
+
${cliCommands.length} commands, ${entityTypes.length} entity keywords`;
|
|
85
|
+
}
|
|
86
|
+
function extractDistribution(spec) {
|
|
87
|
+
const allDistributions = [];
|
|
88
|
+
if (spec?.distributions) {
|
|
89
|
+
const entries = Array.isArray(spec.distributions) ? spec.distributions : Object.entries(spec.distributions).map(([name, data]) => ({ name, ...data }));
|
|
90
|
+
allDistributions.push(...entries);
|
|
91
|
+
}
|
|
92
|
+
const components = spec?.components || {};
|
|
93
|
+
const componentList = Array.isArray(components) ? components : Object.values(components);
|
|
94
|
+
for (const comp of componentList) {
|
|
95
|
+
const distributions = comp?.distributions;
|
|
96
|
+
if (!distributions) continue;
|
|
97
|
+
const distEntries = Array.isArray(distributions) ? distributions : Object.entries(distributions).map(([name, data]) => ({ name, ...data }));
|
|
98
|
+
allDistributions.push(...distEntries);
|
|
99
|
+
}
|
|
100
|
+
for (const dist of allDistributions) {
|
|
101
|
+
if (dist.type === "ide") return dist;
|
|
102
|
+
}
|
|
103
|
+
return allDistributions.length > 0 ? allDistributions[0] : null;
|
|
104
|
+
}
|
|
105
|
+
function extractCLICommands(spec) {
|
|
106
|
+
const commands = [];
|
|
107
|
+
const components = spec?.components || {};
|
|
108
|
+
const componentList = Array.isArray(components) ? components : Object.entries(components).map(([name, data]) => ({ name, ...data }));
|
|
109
|
+
for (const comp of componentList) {
|
|
110
|
+
const cliCommands = comp?.commands;
|
|
111
|
+
if (!cliCommands) continue;
|
|
112
|
+
for (const [rootName, rootDef] of Object.entries(cliCommands)) {
|
|
113
|
+
const subcommands = rootDef?.subcommands || {};
|
|
114
|
+
for (const [subName, subDef] of Object.entries(subcommands)) {
|
|
115
|
+
const sub = subDef;
|
|
116
|
+
const nestedSubs = sub?.subcommands;
|
|
117
|
+
if (nestedSubs) {
|
|
118
|
+
for (const [nestedName, nestedDef] of Object.entries(nestedSubs)) {
|
|
119
|
+
commands.push({
|
|
120
|
+
command: `specverse.${subName}.${nestedName}`,
|
|
121
|
+
title: `${capitalize(subName)} ${capitalize(nestedName)}: ${nestedDef.description || ""}`,
|
|
122
|
+
category: "SpecVerse"
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
commands.push({
|
|
127
|
+
command: `specverse.${subName}`,
|
|
128
|
+
title: `${capitalize(subName)}: ${sub.description || ""}`,
|
|
129
|
+
category: "SpecVerse"
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return commands;
|
|
136
|
+
}
|
|
137
|
+
function extractEntityTypes(spec) {
|
|
138
|
+
const types = /* @__PURE__ */ new Set();
|
|
139
|
+
const components = spec?.components || [];
|
|
140
|
+
for (const component of Array.isArray(components) ? components : Object.values(components)) {
|
|
141
|
+
const comp = component;
|
|
142
|
+
const models = comp?.models;
|
|
143
|
+
if (models) {
|
|
144
|
+
if (Array.isArray(models)) {
|
|
145
|
+
models.forEach((m) => types.add(m.name));
|
|
146
|
+
} else {
|
|
147
|
+
Object.keys(models).forEach((name) => types.add(name));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return [...types];
|
|
152
|
+
}
|
|
153
|
+
function generatePackageJson(commands, _entityTypes, specVersion = "1.0.0") {
|
|
154
|
+
return {
|
|
155
|
+
name: "specverse",
|
|
156
|
+
displayName: "SpecVerse",
|
|
157
|
+
description: "SpecVerse specification language support \u2014 syntax highlighting, validation, IntelliSense",
|
|
158
|
+
version: specVersion,
|
|
159
|
+
publisher: "specverse",
|
|
160
|
+
engines: { vscode: "^1.80.0" },
|
|
161
|
+
categories: ["Programming Languages", "Linters", "Snippets"],
|
|
162
|
+
activationEvents: ["onLanguage:specverse"],
|
|
163
|
+
main: "./dist/extension.js",
|
|
164
|
+
contributes: {
|
|
165
|
+
languages: [{
|
|
166
|
+
id: "specverse",
|
|
167
|
+
aliases: ["SpecVerse", "specly"],
|
|
168
|
+
extensions: [".specly", ".specverse"],
|
|
169
|
+
configuration: "./language-configuration.json"
|
|
170
|
+
}],
|
|
171
|
+
grammars: [{
|
|
172
|
+
language: "specverse",
|
|
173
|
+
scopeName: "source.specverse",
|
|
174
|
+
path: "./syntaxes/specverse.tmLanguage.json"
|
|
175
|
+
}],
|
|
176
|
+
jsonValidation: [{
|
|
177
|
+
fileMatch: ["*.specly", "*.specverse"],
|
|
178
|
+
url: "./schemas/specverse-v3-schema.json"
|
|
179
|
+
}],
|
|
180
|
+
themes: [
|
|
181
|
+
{ label: "SpecVerse Dark", uiTheme: "vs-dark", path: "./themes/specverse-complete-theme.json" },
|
|
182
|
+
{ label: "SpecVerse Basic", uiTheme: "vs-dark", path: "./themes/specverse-basic-theme.json" }
|
|
183
|
+
],
|
|
184
|
+
commands: commands.map((c) => ({
|
|
185
|
+
command: c.command,
|
|
186
|
+
title: c.title,
|
|
187
|
+
category: c.category
|
|
188
|
+
}))
|
|
189
|
+
},
|
|
190
|
+
scripts: {
|
|
191
|
+
"vscode:prepublish": "npm run build",
|
|
192
|
+
build: "node scripts/build.js",
|
|
193
|
+
package: "npx @vscode/vsce package"
|
|
194
|
+
},
|
|
195
|
+
devDependencies: {
|
|
196
|
+
"@types/vscode": "^1.80.0",
|
|
197
|
+
"@vscode/vsce": "^3.0.0",
|
|
198
|
+
esbuild: "^0.25.0"
|
|
199
|
+
},
|
|
200
|
+
overrides: {
|
|
201
|
+
// Force the patched version of lodash through @vscode/vsce →
|
|
202
|
+
// @secretlint → @textlint transitive chain (4.17.23 has a
|
|
203
|
+
// code-injection advisory in `_.template`).
|
|
204
|
+
lodash: "^4.18.1"
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function copyRecursive(src, dest) {
|
|
209
|
+
for (const entry of readdirSync(src)) {
|
|
210
|
+
if (entry === "extension.ts") continue;
|
|
211
|
+
const srcPath = join(src, entry);
|
|
212
|
+
const destPath = join(dest, entry);
|
|
213
|
+
if (statSync(srcPath).isDirectory()) {
|
|
214
|
+
if (!existsSync(destPath)) mkdirSync(destPath, { recursive: true });
|
|
215
|
+
copyRecursive(srcPath, destPath);
|
|
216
|
+
} else {
|
|
217
|
+
copyFileSync(srcPath, destPath);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function getStandardCommands() {
|
|
222
|
+
return [
|
|
223
|
+
{ command: "specverse.validate", title: "Validate specification", category: "SpecVerse" },
|
|
224
|
+
{ command: "specverse.infer", title: "Infer full architecture", category: "SpecVerse" },
|
|
225
|
+
{ command: "specverse.realize", title: "Generate code from specification", category: "SpecVerse" },
|
|
226
|
+
{ command: "specverse.init", title: "Initialize new project", category: "SpecVerse" },
|
|
227
|
+
{ command: "specverse.gen.diagrams", title: "Generate Mermaid diagrams", category: "SpecVerse" },
|
|
228
|
+
{ command: "specverse.gen.docs", title: "Generate documentation", category: "SpecVerse" },
|
|
229
|
+
{ command: "specverse.gen.uml", title: "Generate UML diagrams", category: "SpecVerse" },
|
|
230
|
+
{ command: "specverse.dev.format", title: "Format .specly file", category: "SpecVerse" },
|
|
231
|
+
{ command: "specverse.dev.watch", title: "Watch and validate on change", category: "SpecVerse" },
|
|
232
|
+
{ command: "specverse.dev.quick", title: "Quick validation", category: "SpecVerse" },
|
|
233
|
+
{ command: "specverse.cache", title: "Manage import cache", category: "SpecVerse" },
|
|
234
|
+
{ command: "specverse.ai.docs", title: "Generate AI implementation prompt", category: "SpecVerse" },
|
|
235
|
+
{ command: "specverse.ai.suggest", title: "Get spec improvement suggestions", category: "SpecVerse" },
|
|
236
|
+
{ command: "specverse.ai.template", title: "Load AI prompt template", category: "SpecVerse" }
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
function capitalize(str) {
|
|
240
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
241
|
+
}
|
|
242
|
+
export {
|
|
243
|
+
generateVSCodeExtension as default
|
|
244
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realize/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAMlE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AASnF,cAAM,sBAAuB,YAAW,aAAa;IACnD,IAAI,SAAa;IACjB,OAAO,SAAW;IAClB,YAAY,WAA+E;IAE3F,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAS;IAEtB,UAAU,CAAC,MAAM,CAAC,EAAE;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjB,OAAO,IAAI,UAAU;IAIrB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG;IAK1B,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC;IAKvF;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/realize/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,kBAAkB,CAAC;AAGjC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAMlE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AASnF,cAAM,sBAAuB,YAAW,aAAa;IACnD,IAAI,SAAa;IACjB,OAAO,SAAW;IAClB,YAAY,WAA+E;IAE3F,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAS;IAEtB,UAAU,CAAC,MAAM,CAAC,EAAE;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBjB,OAAO,IAAI,UAAU;IAIrB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG;IAK1B,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC;IAKvF;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA4lB9F;;;OAGG;YACW,UAAU;IAwDxB,OAAO,CAAC,oBAAoB;CAuB7B;AAED,eAAO,MAAM,MAAM,wBAA+B,CAAC;AACnD,eAAe,MAAM,CAAC;AACtB,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
package/dist/realize/index.js
CHANGED
|
@@ -349,6 +349,27 @@ class SpecVerseRealizeEngine {
|
|
|
349
349
|
}
|
|
350
350
|
console.log(` ✅ Routes: ${allModels.length} route handler(s)`);
|
|
351
351
|
}
|
|
352
|
+
// 4.5 Service routes — HTTP endpoints for service operations
|
|
353
|
+
// POST /api/services/{ServiceName}/{operationName}
|
|
354
|
+
// Mirrors app-demo's dynamic runtime URL shape (see
|
|
355
|
+
// specverse-app-demo/src/api/http-server.ts:611) so smoke-parity
|
|
356
|
+
// tests can hit both backends identically.
|
|
357
|
+
if (routeResolved?.instanceFactory?.codeTemplates?.serviceRoutes && servicesList.length > 0) {
|
|
358
|
+
let svcRouteCount = 0;
|
|
359
|
+
for (const service of servicesList) {
|
|
360
|
+
try {
|
|
361
|
+
const svcName = service.name || 'Service';
|
|
362
|
+
const output = await this.codeGenerator.generateFromTemplate(routeResolved, 'serviceRoutes', { spec, service: { name: svcName, ...service }, models: allModels }, { outputDir });
|
|
363
|
+
writeOutput(output);
|
|
364
|
+
svcRouteCount++;
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
errors.push(`Service route ${service?.name}: ${e.message}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (svcRouteCount > 0)
|
|
371
|
+
console.log(` ✅ Service routes: ${svcRouteCount} file(s)`);
|
|
372
|
+
}
|
|
352
373
|
// 5. Views, Forms, Hooks
|
|
353
374
|
const viewsResolved = tryResolve('ui.components');
|
|
354
375
|
if (viewsResolved && spec.views) {
|