@specverse/engines 4.1.5 → 4.1.7

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.
Files changed (122) hide show
  1. package/dist/libs/instance-factories/applications/templates/generic/backend-env-generator.js +22 -0
  2. package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +66 -0
  3. package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +54 -0
  4. package/dist/libs/instance-factories/applications/templates/generic/main-generator.js +290 -0
  5. package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +530 -0
  6. package/dist/libs/instance-factories/applications/templates/react/api-client-generator.js +437 -0
  7. package/dist/libs/instance-factories/applications/templates/react/api-types-generator.js +146 -0
  8. package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +73 -0
  9. package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +18 -0
  10. package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +99 -0
  11. package/dist/libs/instance-factories/applications/templates/react/gitignore-generator.js +35 -0
  12. package/dist/libs/instance-factories/applications/templates/react/index-css-generator.js +9 -0
  13. package/dist/libs/instance-factories/applications/templates/react/index-html-generator.js +23 -0
  14. package/dist/libs/instance-factories/applications/templates/react/main-tsx-generator.js +29 -0
  15. package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +49 -0
  16. package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +156 -0
  17. package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +935 -0
  18. package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +143 -0
  19. package/dist/libs/instance-factories/applications/templates/react/runtime-app-tsx-generator.js +101 -0
  20. package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +50 -0
  21. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +646 -0
  22. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +65 -0
  23. package/dist/libs/instance-factories/applications/templates/react/tsconfig-generator.js +28 -0
  24. package/dist/libs/instance-factories/applications/templates/react/use-api-hooks-generator.js +132 -0
  25. package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +143 -0
  26. package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +143 -0
  27. package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +355 -0
  28. package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +91 -0
  29. package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +79 -0
  30. package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +42 -0
  31. package/dist/libs/instance-factories/cli/templates/commander/cli-bin-wrapper-generator.js +11 -0
  32. package/dist/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +111 -0
  33. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +928 -0
  34. package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +83 -0
  35. package/dist/libs/instance-factories/communication/templates/eventemitter/publisher-generator.js +91 -0
  36. package/dist/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.js +86 -0
  37. package/dist/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.js +93 -0
  38. package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +280 -0
  39. package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +125 -0
  40. package/dist/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.js +25 -0
  41. package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +371 -0
  42. package/dist/libs/instance-factories/orms/templates/prisma/services-generator.js +266 -0
  43. package/dist/libs/instance-factories/scaffolding/templates/generic/env-example-generator.js +51 -0
  44. package/dist/libs/instance-factories/scaffolding/templates/generic/env-generator.js +61 -0
  45. package/dist/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.js +59 -0
  46. package/dist/libs/instance-factories/scaffolding/templates/generic/package-json-generator.js +126 -0
  47. package/dist/libs/instance-factories/scaffolding/templates/generic/readme-generator.js +159 -0
  48. package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +56 -0
  49. package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.js +37 -0
  50. package/dist/libs/instance-factories/sdks/templates/python/sdk-generator.js +29 -0
  51. package/dist/libs/instance-factories/sdks/templates/typescript/sdk-generator.js +28 -0
  52. package/dist/libs/instance-factories/services/templates/memory/generate-interpreter.js +14 -0
  53. package/dist/libs/instance-factories/services/templates/memory/step-conventions-memory.js +415 -0
  54. package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +177 -0
  55. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +413 -0
  56. package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +243 -0
  57. package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +264 -0
  58. package/dist/libs/instance-factories/services/templates/shared-patterns.js +24 -0
  59. package/dist/libs/instance-factories/shared/path-resolver.js +59 -0
  60. package/dist/libs/instance-factories/storage/templates/mongodb/config-generator.js +13 -0
  61. package/dist/libs/instance-factories/storage/templates/mongodb/docker-generator.js +16 -0
  62. package/dist/libs/instance-factories/storage/templates/postgresql/config-generator.js +45 -0
  63. package/dist/libs/instance-factories/storage/templates/postgresql/docker-generator.js +46 -0
  64. package/dist/libs/instance-factories/storage/templates/redis/config-generator.js +14 -0
  65. package/dist/libs/instance-factories/storage/templates/redis/docker-generator.js +16 -0
  66. package/dist/libs/instance-factories/test-generation.js +145 -0
  67. package/dist/libs/instance-factories/testing/templates/vitest/tests-generator.js +30 -0
  68. package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +149 -0
  69. package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +232 -0
  70. package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +49 -0
  71. package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +18 -0
  72. package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
  73. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +97 -0
  74. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +64 -0
  75. package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +182 -0
  76. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +1210 -0
  77. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +172 -0
  78. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +240 -0
  79. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +147 -0
  80. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +281 -0
  81. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +409 -0
  82. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +414 -0
  83. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +467 -0
  84. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +135 -0
  85. package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
  86. package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +965 -0
  87. package/dist/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.js +238 -0
  88. package/dist/libs/instance-factories/validation/templates/zod/validation-generator.js +25 -0
  89. package/dist/libs/instance-factories/views/index.js +48 -0
  90. package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +742 -0
  91. package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +824 -0
  92. package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +719 -0
  93. package/dist/libs/instance-factories/views/templates/react/app-generator.js +45 -0
  94. package/dist/libs/instance-factories/views/templates/react/components-generator.js +779 -0
  95. package/dist/libs/instance-factories/views/templates/react/forms-generator.js +285 -0
  96. package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +46 -0
  97. package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +111 -0
  98. package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +9 -0
  99. package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +23 -0
  100. package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +21 -0
  101. package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +299 -0
  102. package/dist/libs/instance-factories/views/templates/react/router-generator.js +136 -0
  103. package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +107 -0
  104. package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +179 -0
  105. package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +7 -0
  106. package/dist/libs/instance-factories/views/templates/react/types-generator.js +56 -0
  107. package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +27 -0
  108. package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +29 -0
  109. package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +261 -0
  110. package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +34 -0
  111. package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +800 -0
  112. package/dist/libs/instance-factories/views/templates/shared/base-generator.js +305 -0
  113. package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +517 -0
  114. package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
  115. package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +445 -0
  116. package/dist/libs/instance-factories/views/templates/shared/index.js +80 -0
  117. package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +210 -0
  118. package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +492 -0
  119. package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +321 -0
  120. package/dist/realize/index.js +36 -12
  121. package/dist/realize/index.js.map +1 -1
  122. package/package.json +3 -2
@@ -0,0 +1,83 @@
1
+ function generateEventBus(context) {
2
+ const { spec } = context;
3
+ const events = spec.events ? Object.keys(spec.events) : [];
4
+ return `/**
5
+ * Event Bus
6
+ * In-memory event bus using EventEmitter3
7
+ * Generated from SpecVerse specification
8
+ */
9
+
10
+ import EventEmitter from 'eventemitter3';
11
+
12
+ // Event type definitions
13
+ ${events.map((event) => `export type ${event}Payload = any; // TODO: Define payload type`).join("\n")}
14
+
15
+ // Event names enum
16
+ export enum EventName {
17
+ ${events.map((event) => ` ${event} = '${event}',`).join("\n")}
18
+ }
19
+
20
+ /**
21
+ * Event Bus Singleton
22
+ */
23
+ class EventBus extends EventEmitter {
24
+ private static instance: EventBus;
25
+
26
+ private constructor() {
27
+ super();
28
+ this.setMaxListeners(100); // Configure max listeners
29
+ }
30
+
31
+ /**
32
+ * Get singleton instance
33
+ */
34
+ public static getInstance(): EventBus {
35
+ if (!EventBus.instance) {
36
+ EventBus.instance = new EventBus();
37
+ }
38
+ return EventBus.instance;
39
+ }
40
+
41
+ /**
42
+ * Publish an event
43
+ */
44
+ public publish<T = any>(event: EventName | string, payload: T): void {
45
+ console.log(\`[EventBus] Publishing event: \${event}\`, payload);
46
+ this.emit(event, payload);
47
+ }
48
+
49
+ /**
50
+ * Subscribe to an event
51
+ */
52
+ public subscribe<T = any>(
53
+ event: EventName | string,
54
+ handler: (payload: T) => void | Promise<void>
55
+ ): () => void {
56
+ console.log(\`[EventBus] Subscribing to event: \${event}\`);
57
+ this.on(event, handler);
58
+
59
+ // Return unsubscribe function
60
+ return () => {
61
+ this.off(event, handler);
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Subscribe to an event (one-time)
67
+ */
68
+ public subscribeOnce<T = any>(
69
+ event: EventName | string,
70
+ handler: (payload: T) => void | Promise<void>
71
+ ): void {
72
+ this.once(event, handler);
73
+ }
74
+ }
75
+
76
+ // Export singleton instance
77
+ export const eventBus = EventBus.getInstance();
78
+ export default eventBus;
79
+ `;
80
+ }
81
+ export {
82
+ generateEventBus as default
83
+ };
@@ -0,0 +1,91 @@
1
+ function generateEventPublisher(context) {
2
+ const { event, spec } = context;
3
+ if (!event) {
4
+ throw new Error("Event is required in template context");
5
+ }
6
+ const eventName = event.name;
7
+ const publisherName = `${eventName}Publisher`;
8
+ return `/**
9
+ * ${publisherName}
10
+ * Publisher for ${eventName} events
11
+ * ${event.description || ""}
12
+ */
13
+
14
+ import { eventBus, EventName } from '../eventBus.js';
15
+
16
+ export interface ${eventName}Payload {
17
+ ${generatePayloadInterface(event)}
18
+ }
19
+
20
+ /**
21
+ * ${publisherName} class
22
+ */
23
+ export class ${publisherName} {
24
+ /**
25
+ * Publish ${eventName} event
26
+ */
27
+ public async publish(payload: ${eventName}Payload): Promise<void> {
28
+ try {
29
+ // Validate payload
30
+ this.validate(payload);
31
+
32
+ // Add timestamp if not present
33
+ const enrichedPayload = {
34
+ ...payload,
35
+ timestamp: payload.timestamp || new Date().toISOString(),
36
+ };
37
+
38
+ // Publish event
39
+ eventBus.publish(EventName.${eventName}, enrichedPayload);
40
+
41
+ console.log(\`[${publisherName}] Published event\`, enrichedPayload);
42
+ } catch (error) {
43
+ console.error(\`[${publisherName}] Failed to publish event\`, error);
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Validate event payload
50
+ */
51
+ private validate(payload: ${eventName}Payload): void {
52
+ // TODO: Add validation logic
53
+ ${generateValidation(event)}
54
+ }
55
+ }
56
+
57
+ // Export singleton instance
58
+ export const ${eventName.toLowerCase()}Publisher = new ${publisherName}();
59
+ export default ${eventName.toLowerCase()}Publisher;
60
+ `;
61
+ }
62
+ function generatePayloadInterface(event) {
63
+ const attributes = event.attributes || {};
64
+ const fields = [];
65
+ for (const [name, config] of Object.entries(attributes)) {
66
+ const attr = config;
67
+ const type = attr.type || "String";
68
+ let tsType = "string";
69
+ if (type === "Integer" || type === "Number") tsType = "number";
70
+ if (type === "Boolean") tsType = "boolean";
71
+ if (type === "DateTime") tsType = "string";
72
+ if (type === "UUID") tsType = "string";
73
+ const optional = attr.required ? "" : "?";
74
+ fields.push(` ${name}${optional}: ${tsType};`);
75
+ }
76
+ return fields.join("\n") || " // No attributes defined";
77
+ }
78
+ function generateValidation(event) {
79
+ const attributes = event.attributes || {};
80
+ const validations = [];
81
+ for (const [name, config] of Object.entries(attributes)) {
82
+ const attr = config;
83
+ if (attr.required) {
84
+ validations.push(` if (!payload.${name}) throw new Error('${name} is required');`);
85
+ }
86
+ }
87
+ return validations.join("\n") || " // No validation rules";
88
+ }
89
+ export {
90
+ generateEventPublisher as default
91
+ };
@@ -0,0 +1,86 @@
1
+ function generateEventSubscriber(context) {
2
+ const { event, service, spec } = context;
3
+ if (!event) {
4
+ throw new Error("Event is required in template context");
5
+ }
6
+ const eventName = event.name;
7
+ const subscriberName = `${eventName}Subscriber`;
8
+ return `/**
9
+ * ${subscriberName}
10
+ * Subscriber for ${eventName} events
11
+ * ${event.description || ""}
12
+ */
13
+
14
+ import { eventBus, EventName, type ${eventName}Payload } from '../eventBus.js';
15
+
16
+ /**
17
+ * ${subscriberName} class
18
+ */
19
+ export class ${subscriberName} {
20
+ private unsubscribe?: () => void;
21
+
22
+ /**
23
+ * Start listening for ${eventName} events
24
+ */
25
+ public start(): void {
26
+ console.log(\`[${subscriberName}] Starting subscriber\`);
27
+
28
+ this.unsubscribe = eventBus.subscribe<${eventName}Payload>(
29
+ EventName.${eventName},
30
+ this.handleEvent.bind(this)
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Stop listening for ${eventName} events
36
+ */
37
+ public stop(): void {
38
+ if (this.unsubscribe) {
39
+ console.log(\`[${subscriberName}] Stopping subscriber\`);
40
+ this.unsubscribe();
41
+ this.unsubscribe = undefined;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Handle ${eventName} event
47
+ */
48
+ private async handleEvent(payload: ${eventName}Payload): Promise<void> {
49
+ try {
50
+ console.log(\`[${subscriberName}] Received event\`, payload);
51
+
52
+ // Process event
53
+ await this.processEvent(payload);
54
+
55
+ console.log(\`[${subscriberName}] Event processed successfully\`);
56
+ } catch (error) {
57
+ console.error(\`[${subscriberName}] Failed to process event\`, error);
58
+ // TODO: Add error handling (dead letter queue, retry logic, etc.)
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Process ${eventName} event
64
+ */
65
+ private async processEvent(payload: ${eventName}Payload): Promise<void> {
66
+ // TODO: Implement event processing logic
67
+ ${generateProcessingLogic(event)}
68
+ }
69
+ }
70
+
71
+ // Export singleton instance
72
+ export const ${eventName.toLowerCase()}Subscriber = new ${subscriberName}();
73
+ export default ${eventName.toLowerCase()}Subscriber;
74
+ `;
75
+ }
76
+ function generateProcessingLogic(event) {
77
+ return `
78
+ // Example: Update database, send notifications, trigger workflows, etc.
79
+ console.log('Processing ${event.name} event:', payload);
80
+
81
+ // Add your business logic here
82
+ `;
83
+ }
84
+ export {
85
+ generateEventSubscriber as default
86
+ };
@@ -0,0 +1,93 @@
1
+ function generateMetaRoutes(context) {
2
+ const { spec } = context;
3
+ return `/**
4
+ * Meta API Routes
5
+ *
6
+ * Provides spec and view metadata endpoints for the frontend
7
+ */
8
+
9
+ import { FastifyInstance, FastifyPluginOptions } from 'fastify';
10
+ import { readFile } from 'fs/promises';
11
+ import { join } from 'path';
12
+ import { parse } from 'yaml';
13
+
14
+ export default async function MetaRoutes(
15
+ fastify: FastifyInstance,
16
+ options: FastifyPluginOptions
17
+ ) {
18
+ // Get specification
19
+ fastify.get('/spec', async (request, reply) => {
20
+ try {
21
+ const specPath = join(process.cwd(), '../../../specs/main.specly');
22
+ const specContent = await readFile(specPath, 'utf-8');
23
+ const spec = parse(specContent);
24
+ return spec;
25
+ } catch (error: any) {
26
+ reply.status(500).send({ error: \`Failed to load spec: \${error.message}\` });
27
+ }
28
+ });
29
+
30
+ // Get all views
31
+ fastify.get('/views', async (request, reply) => {
32
+ try {
33
+ const specPath = join(process.cwd(), '../../../specs/main.specly');
34
+ const specContent = await readFile(specPath, 'utf-8');
35
+ const spec = parse(specContent);
36
+
37
+ // Extract views from the spec
38
+ const views = [];
39
+ if (spec.components && typeof spec.components === 'object') {
40
+ // components is an object with component names as keys
41
+ for (const [componentName, component] of Object.entries(spec.components)) {
42
+ if (component && typeof component === 'object' && 'views' in component) {
43
+ const componentViews = component.views as Record<string, any>;
44
+ for (const [viewName, viewDef] of Object.entries(componentViews)) {
45
+ views.push({
46
+ name: viewName,
47
+ ...viewDef as any
48
+ });
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ return { views };
55
+ } catch (error: any) {
56
+ reply.status(500).send({ error: \`Failed to load views: \${error.message}\` });
57
+ }
58
+ });
59
+
60
+ // Get individual view by name
61
+ fastify.get('/views/:viewName', async (request, reply) => {
62
+ try {
63
+ const { viewName } = request.params as { viewName: string };
64
+ const specPath = join(process.cwd(), '../../../specs/main.specly');
65
+ const specContent = await readFile(specPath, 'utf-8');
66
+ const spec = parse(specContent);
67
+
68
+ // Find the view
69
+ if (spec.components && typeof spec.components === 'object') {
70
+ for (const [componentName, component] of Object.entries(spec.components)) {
71
+ if (component && typeof component === 'object' && 'views' in component) {
72
+ const componentViews = component.views as Record<string, any>;
73
+ if (componentViews[viewName]) {
74
+ return {
75
+ name: viewName,
76
+ ...componentViews[viewName]
77
+ };
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ reply.status(404).send({ error: \`View \${viewName} not found\` });
84
+ } catch (error: any) {
85
+ reply.status(500).send({ error: \`Failed to load view: \${error.message}\` });
86
+ }
87
+ });
88
+ }
89
+ `;
90
+ }
91
+ export {
92
+ generateMetaRoutes as default
93
+ };
@@ -0,0 +1,280 @@
1
+ function generateFastifyRoutes(context) {
2
+ const { controller, model, spec, implType } = context;
3
+ if (!controller) {
4
+ throw new Error("Controller is required in template context");
5
+ }
6
+ const modelName = model?.name || controller.modelReference;
7
+ const controllerName = controller.name;
8
+ const routeName = controllerName || `${modelName}Controller`;
9
+ const isModelController = !!modelName;
10
+ const handlerName = isModelController ? `${modelName}Controller` : controllerName;
11
+ const imports = generateImports(controller, modelName, handlerName, isModelController, implType);
12
+ let endpoints = controller.endpoints;
13
+ if (!endpoints || endpoints.length === 0) {
14
+ if (controller.cured) {
15
+ endpoints = curedToEndpoints(controller.cured, modelName);
16
+ }
17
+ }
18
+ if (!endpoints || endpoints.length === 0) {
19
+ console.warn(`Warning: Controller ${controllerName} has no endpoints. Generating empty routes file.`);
20
+ }
21
+ const routeHandlers = endpoints?.map((endpoint) => {
22
+ return generateRouteHandler(endpoint, modelName, handlerName, isModelController, implType, controllerName);
23
+ }).join("\n\n") || "";
24
+ return `${imports}
25
+
26
+ /**
27
+ * ${routeName} Routes
28
+ * Generated from SpecVerse specification
29
+ *
30
+ * ${isModelController ? `Model: ${modelName}` : `Service: ${controllerName}`}
31
+ * Operations: ${controller.endpoints?.map((e) => e.operation).join(", ") || "CURED"}
32
+ */
33
+ export default async function ${routeName.replace("Controller", "")}Routes(
34
+ fastify: FastifyInstance,
35
+ options: any
36
+ ) {
37
+ const handler = ${isModelController ? "options.controllers" : "options.services"}.${handlerName};
38
+
39
+ ${routeHandlers.split("\n").map((line) => " " + line).join("\n")}
40
+ }
41
+ `;
42
+ }
43
+ function generateImports(controller, modelName, handlerName, isModelController, implType) {
44
+ const imports = [
45
+ `import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';`
46
+ ];
47
+ if (implType?.technology?.validation === "zod" && modelName) {
48
+ imports.push(`import { ${modelName}Schema } from '../validation/${modelName}.schema.js';`);
49
+ }
50
+ return imports.join("\n");
51
+ }
52
+ function generateRouteHandler(endpoint, modelName, handlerName, isModelController, implType, controllerName) {
53
+ let operation = endpoint.operation || endpoint.name;
54
+ if (!operation || operation === "custom") {
55
+ const serviceOp = endpoint.serviceOperation?.type;
56
+ if (serviceOp === "custom" && endpoint.path) {
57
+ const pathParts = endpoint.path.split("/").filter((p) => p);
58
+ const lastPart = pathParts[pathParts.length - 1];
59
+ operation = lastPart.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
60
+ } else if (!operation) {
61
+ operation = serviceOp;
62
+ }
63
+ }
64
+ if (!operation) {
65
+ operation = inferOperationFromMethodAndPath(endpoint.method, endpoint.path);
66
+ }
67
+ if (!operation || operation === "unknown") {
68
+ console.warn(`Warning: Could not determine operation for endpoint in ${controllerName || "controller"}. Endpoint data:`, {
69
+ operation: endpoint.operation,
70
+ name: endpoint.name,
71
+ method: endpoint.method,
72
+ path: endpoint.path,
73
+ serviceOperation: endpoint.serviceOperation
74
+ });
75
+ }
76
+ const method = endpoint.method?.toLowerCase() || inferHttpMethod(operation);
77
+ const path = inferPath(operation, endpoint);
78
+ const handler = generateHandlerBody(operation, modelName, handlerName, isModelController, implType);
79
+ let route = `// ${operation} ${modelName}
80
+ `;
81
+ route += `fastify.${method}('${path}', {
82
+ `;
83
+ if (implType?.technology?.validation === "zod") {
84
+ route += ` schema: {
85
+ `;
86
+ if (operation === "create" || operation === "update" || operation === "evolve") {
87
+ route += ` body: ${modelName}Schema.${operation},
88
+ `;
89
+ }
90
+ if (operation === "retrieve" || operation === "update" || operation === "delete") {
91
+ route += ` params: ${modelName}Schema.params,
92
+ `;
93
+ }
94
+ route += ` response: {
95
+ `;
96
+ route += ` 200: ${modelName}Schema.response
97
+ `;
98
+ route += ` }
99
+ `;
100
+ route += ` },
101
+ `;
102
+ }
103
+ const usesRequest = ["create", "retrieve", "update", "evolve", "delete", "validate"].includes(operation) || !["list"].includes(operation);
104
+ const requestParam = usesRequest ? "request" : "_request";
105
+ route += ` handler: async (${requestParam}: FastifyRequest, reply: FastifyReply) => {
106
+ `;
107
+ route += handler.split("\n").map((line) => " " + line).join("\n") + "\n";
108
+ route += ` }
109
+ `;
110
+ route += `});`;
111
+ return route;
112
+ }
113
+ function generateHandlerBody(operation, modelName, handlerName, isModelController, implType) {
114
+ const rawLowerModel = modelName?.toLowerCase() || "item";
115
+ const RESERVED_WORDS = /* @__PURE__ */ new Set(["import", "export", "default", "class", "function", "return", "delete", "new", "this", "switch", "case", "break", "continue", "for", "while", "do", "if", "else", "try", "catch", "finally", "throw", "typeof", "instanceof", "in", "of", "let", "const", "var", "void", "with", "yield", "async", "await", "enum", "implements", "interface", "package", "private", "protected", "public", "static", "super", "extends"]);
116
+ const lowerModel = RESERVED_WORDS.has(rawLowerModel) ? `${rawLowerModel}Item` : rawLowerModel;
117
+ const operationMap = {
118
+ "custom": "create",
119
+ // POST without ID -> create
120
+ "findUnique": "retrieve",
121
+ // GET with ID -> retrieve
122
+ "findMany": "list",
123
+ // GET without ID -> list
124
+ "updateUnique": "update",
125
+ // PUT with ID -> update
126
+ "deleteUnique": "delete"
127
+ // DELETE with ID -> delete
128
+ };
129
+ const mappedOperation = operationMap[operation] || operation;
130
+ switch (mappedOperation) {
131
+ case "create":
132
+ return `try {
133
+ const ${lowerModel} = await handler.create(request.body as any);
134
+ return reply.status(201).send(${lowerModel});
135
+ } catch (error) {
136
+ return reply.status(400).send({
137
+ error: 'Failed to create ${lowerModel}',
138
+ message: error instanceof Error ? error.message : String(error)
139
+ });
140
+ }`;
141
+ case "retrieve":
142
+ return `try {
143
+ const { id } = request.params as { id: string };
144
+ const ${lowerModel} = await handler.retrieve(id);
145
+
146
+ if (!${lowerModel}) {
147
+ return reply.status(404).send({ error: '${modelName} not found' });
148
+ }
149
+
150
+ return reply.send(${lowerModel});
151
+ } catch (error) {
152
+ return reply.status(500).send({
153
+ error: 'Failed to retrieve ${lowerModel}',
154
+ message: error instanceof Error ? error.message : String(error)
155
+ });
156
+ }`;
157
+ case "update":
158
+ case "evolve":
159
+ return `try {
160
+ const { id } = request.params as { id: string };
161
+ const ${lowerModel} = await handler.${operation}(id, request.body as any);
162
+ return reply.send(${lowerModel});
163
+ } catch (error) {
164
+ return reply.status(400).send({
165
+ error: 'Failed to ${operation} ${lowerModel}',
166
+ message: error instanceof Error ? error.message : String(error)
167
+ });
168
+ }`;
169
+ case "delete":
170
+ return `try {
171
+ const { id } = request.params as { id: string };
172
+ await handler.delete(id);
173
+ return reply.status(204).send();
174
+ } catch (error) {
175
+ return reply.status(500).send({
176
+ error: 'Failed to delete ${lowerModel}',
177
+ message: error instanceof Error ? error.message : String(error)
178
+ });
179
+ }`;
180
+ case "list":
181
+ return `try {
182
+ const ${lowerModel}s = await handler.retrieveAll();
183
+ return reply.send(${lowerModel}s);
184
+ } catch (error) {
185
+ return reply.status(500).send({
186
+ error: 'Failed to list ${lowerModel}s',
187
+ message: error instanceof Error ? error.message : String(error)
188
+ });
189
+ }`;
190
+ case "validate":
191
+ return `try {
192
+ const { data, operation: op } = request.body as { data: any; operation: string };
193
+ const result = handler.validate(data, { operation: op });
194
+ return reply.send(result);
195
+ } catch (error) {
196
+ return reply.status(400).send({
197
+ error: 'Validation failed',
198
+ message: error instanceof Error ? error.message : String(error)
199
+ });
200
+ }`;
201
+ default:
202
+ return `try {
203
+ const result = await handler.${operation}(request.body as any);
204
+ return reply.send(result);
205
+ } catch (error) {
206
+ return reply.status(400).send({
207
+ error: 'Failed to execute ${operation}',
208
+ message: error instanceof Error ? error.message : String(error)
209
+ });
210
+ }`;
211
+ }
212
+ }
213
+ function inferOperationFromMethodAndPath(method, path) {
214
+ if (!method || !path) return "unknown";
215
+ const methodLower = method.toLowerCase();
216
+ const hasIdParam = path.includes(":id");
217
+ if (methodLower === "post" && !hasIdParam) return "create";
218
+ if (methodLower === "get" && hasIdParam) return "retrieve";
219
+ if (methodLower === "get" && !hasIdParam) return "list";
220
+ if (methodLower === "put" && hasIdParam) return "update";
221
+ if (methodLower === "patch" && hasIdParam) return "evolve";
222
+ if (methodLower === "delete" && hasIdParam) return "delete";
223
+ return "unknown";
224
+ }
225
+ function inferHttpMethod(operation) {
226
+ if (!operation) {
227
+ console.warn("Warning: undefined operation in inferHttpMethod");
228
+ return "post";
229
+ }
230
+ const opLower = operation.toLowerCase();
231
+ if (opLower === "create" || opLower === "validate") return "post";
232
+ if (opLower === "retrieve" || opLower === "list") return "get";
233
+ if (opLower === "update" || opLower === "evolve") return "put";
234
+ if (opLower === "delete") return "delete";
235
+ return "post";
236
+ }
237
+ function inferPath(operation, endpoint) {
238
+ const opLower = operation.toLowerCase();
239
+ if (opLower === "create") return "/";
240
+ if (opLower === "list") return "/";
241
+ if (opLower === "retrieve") return "/:id";
242
+ if (opLower === "update") return "/:id";
243
+ if (opLower === "evolve") return "/:id/evolve";
244
+ if (opLower === "delete") return "/:id";
245
+ if (opLower === "validate") return "/validate";
246
+ if (endpoint.path && endpoint.serviceOperation?.type === "custom") {
247
+ const pathParts = endpoint.path.split("/").filter((p) => p);
248
+ const lastPart = pathParts[pathParts.length - 1];
249
+ return `/${lastPart}`;
250
+ }
251
+ return `/${opLower}`;
252
+ }
253
+ function curedToEndpoints(cured, modelName) {
254
+ const endpoints = [];
255
+ if (cured.create) {
256
+ endpoints.push({ operation: "create", method: "POST" });
257
+ }
258
+ if (cured.retrieve) {
259
+ endpoints.push({ operation: "retrieve", method: "GET" });
260
+ }
261
+ if (cured.retrieve_many) {
262
+ endpoints.push({ operation: "list", method: "GET" });
263
+ }
264
+ if (cured.update) {
265
+ endpoints.push({ operation: "update", method: "PUT" });
266
+ }
267
+ if (cured.evolve) {
268
+ endpoints.push({ operation: "evolve", method: "PATCH" });
269
+ }
270
+ if (cured.delete) {
271
+ endpoints.push({ operation: "delete", method: "DELETE" });
272
+ }
273
+ if (cured.validate) {
274
+ endpoints.push({ operation: "validate", method: "POST" });
275
+ }
276
+ return endpoints;
277
+ }
278
+ export {
279
+ generateFastifyRoutes as default
280
+ };