@specverse/engines 4.1.4 → 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.
Files changed (123) 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.d.ts.map +1 -1
  121. package/dist/realize/index.js +2 -0
  122. package/dist/realize/index.js.map +1 -1
  123. package/package.json +3 -2
@@ -0,0 +1,415 @@
1
+ import { CONVENTION_PATTERNS } from "../shared-patterns.js";
2
+ function generateInterpreterModule() {
3
+ const lines = [];
4
+ lines.push(`/**
5
+ * Generated Behavior Functions \u2014 targeting DynamicModelStore
6
+ *
7
+ * AUTO-GENERATED by realize engine memory factory.
8
+ * DO NOT EDIT \u2014 modify step-conventions-memory.ts instead.
9
+ *
10
+ * Convention count: ${CONVENTION_PATTERNS.length}
11
+ */
12
+
13
+ import type { DynamicModelStore, Entity } from '../stores/dynamic-model-store.js';
14
+ import type { EventBus } from './event-bus.js';
15
+
16
+ export interface RuntimeContext {
17
+ modelName: string;
18
+ store: DynamicModelStore;
19
+ eventBus: EventBus;
20
+ entity?: Entity;
21
+ params: Record<string, any>;
22
+ operationName: string;
23
+ }
24
+
25
+ /** Resolve entity ID from params \u2014 tries id, modelId (e.g. pollId), entity */
26
+ function resolveId(model: string, ctx: RuntimeContext): string | undefined {
27
+ const modelIdKey = model.charAt(0).toLowerCase() + model.slice(1) + 'Id';
28
+ return ctx.params.id || ctx.params[modelIdKey] || ctx.entity?.id;
29
+ }
30
+
31
+ export interface ConventionHandler {
32
+ name: string;
33
+ pattern: RegExp;
34
+ execute: (match: RegExpMatchArray, ctx: RuntimeContext) => Promise<any>;
35
+ }
36
+
37
+ export const GENERATED_CONVENTIONS: ConventionHandler[] = [
38
+ `);
39
+ lines.push(generateValidate());
40
+ lines.push(generateCheckNoExisting());
41
+ lines.push(generateCheck());
42
+ lines.push(generateFind());
43
+ lines.push(generateFindAllFor());
44
+ lines.push(generateCountPer());
45
+ lines.push(generateCreate());
46
+ lines.push(generateUpdateField());
47
+ lines.push(generateUpdate());
48
+ lines.push(generateDelete());
49
+ lines.push(generateTransition());
50
+ lines.push(generateSet());
51
+ lines.push(generateIncrement());
52
+ lines.push(generateDecrement());
53
+ lines.push(generateCalculate());
54
+ lines.push(generateSendEvent());
55
+ lines.push(generateSendNotification());
56
+ lines.push(generateCallService());
57
+ lines.push(generateReturn());
58
+ lines.push(`];
59
+
60
+ /**
61
+ * Match a step against conventions and execute.
62
+ * Returns { executed, skipped, result }.
63
+ */
64
+ export async function executeStep(
65
+ step: string,
66
+ ctx: RuntimeContext
67
+ ): Promise<{ matched: boolean; convention?: string; result?: any; error?: string }> {
68
+ for (const convention of GENERATED_CONVENTIONS) {
69
+ const match = step.match(convention.pattern);
70
+ if (match) {
71
+ try {
72
+ const result = await convention.execute(match, ctx);
73
+ return { matched: true, convention: convention.name, result };
74
+ } catch (error: any) {
75
+ return { matched: true, convention: convention.name, error: error.message };
76
+ }
77
+ }
78
+ }
79
+ return { matched: false };
80
+ }
81
+ `);
82
+ return lines.join("\n");
83
+ }
84
+ function p(pattern) {
85
+ const src = CONVENTION_PATTERNS.find((p2) => p2.name === pattern)?.pattern.source || "";
86
+ const flags = CONVENTION_PATTERNS.find((p2) => p2.name === pattern)?.pattern.flags || "i";
87
+ return `/${src}/${flags}`;
88
+ }
89
+ function generateValidate() {
90
+ return ` {
91
+ name: 'validate',
92
+ pattern: ${p("validate")},
93
+ execute: async (m, ctx) => {
94
+ const target = m[1];
95
+ const data = ctx.params.data || ctx.params;
96
+ if (!data || Object.keys(data).length === 0) {
97
+ throw new Error(\`Validation failed: no data provided for \${target}\`);
98
+ }
99
+ return { valid: true, target };
100
+ },
101
+ },`;
102
+ }
103
+ function generateCheckNoExisting() {
104
+ return ` {
105
+ name: 'check-no-existing',
106
+ pattern: ${p("check-no-existing")},
107
+ execute: async (m, ctx) => {
108
+ const checkModel = m[1].charAt(0).toUpperCase() + m[1].slice(1);
109
+ const entityA = m[2].charAt(0).toUpperCase() + m[2].slice(1);
110
+ const entityB = m[3].charAt(0).toUpperCase() + m[3].slice(1);
111
+ const fkA = entityA.charAt(0).toLowerCase() + entityA.slice(1) + 'Id';
112
+ const fkB = entityB.charAt(0).toLowerCase() + entityB.slice(1) + 'Id';
113
+ const idA = ctx.params[fkA] || resolveId(entityA, ctx);
114
+ const idB = ctx.params[fkB] || resolveId(entityB, ctx);
115
+ if (!idA || !idB) return true;
116
+ const all = await ctx.store.query(checkModel, {});
117
+ const existing = all.entities.find((e: any) =>
118
+ (e.data?.[fkA] === idA) && (e.data?.[fkB] === idB)
119
+ );
120
+ if (existing) {
121
+ throw new Error(\`\${entityA} already has a \${checkModel} for this \${entityB}\`);
122
+ }
123
+ return true;
124
+ },
125
+ },`;
126
+ }
127
+ function generateCheck() {
128
+ return ` {
129
+ name: 'check',
130
+ pattern: ${p("check")},
131
+ execute: async (m, ctx) => {
132
+ const condition = m[1];
133
+ if (ctx.entity) {
134
+ const data = ctx.entity.data || {};
135
+ const fieldMatch = condition.match(/(\\w+)/i);
136
+ if (fieldMatch && data[fieldMatch[1]] !== undefined) {
137
+ return !!data[fieldMatch[1]];
138
+ }
139
+ }
140
+ return true;
141
+ },
142
+ },`;
143
+ }
144
+ function generateFind() {
145
+ return ` {
146
+ name: 'find',
147
+ pattern: ${p("find")},
148
+ execute: async (m, ctx) => {
149
+ const model = m[1];
150
+ const field = m[2];
151
+ if (field === 'id') {
152
+ const id = resolveId(model, ctx);
153
+ if (!id) throw new Error(\`No id to find \${model}\`);
154
+ const entity = await ctx.store.retrieve(model, id);
155
+ if (!entity) throw new Error(\`\${model} not found by id: \${id}\`);
156
+ return entity;
157
+ }
158
+ const value = ctx.params[field] || ctx.params.data?.[field];
159
+ const result = await ctx.store.query(model, { filters: { [field]: value }, limit: 1 });
160
+ if (result.entities.length === 0) throw new Error(\`\${model} not found by \${field}: \${value}\`);
161
+ return result.entities[0];
162
+ },
163
+ },`;
164
+ }
165
+ function generateFindAllFor() {
166
+ return ` {
167
+ name: 'find-all-for',
168
+ pattern: ${p("find-all-for")},
169
+ execute: async (m, ctx) => {
170
+ const targetModel = m[1].charAt(0).toUpperCase() + m[1].slice(1);
171
+ const sourceModel = m[2].charAt(0).toUpperCase() + m[2].slice(1);
172
+ const sourceId = resolveId(sourceModel, ctx);
173
+ if (!sourceId) throw new Error(\`No id to find \${targetModel}s for \${sourceModel}\`);
174
+ const fk = sourceModel.charAt(0).toLowerCase() + sourceModel.slice(1) + 'Id';
175
+ const result = await ctx.store.query(targetModel, { filters: { [fk]: sourceId } });
176
+ if (result.entities.length === 0) {
177
+ const allResult = await ctx.store.query(targetModel, {});
178
+ return allResult.entities.filter((e: any) => e.data?.[fk] === sourceId);
179
+ }
180
+ return result.entities;
181
+ },
182
+ },`;
183
+ }
184
+ function generateCountPer() {
185
+ return ` {
186
+ name: 'count-per',
187
+ pattern: ${p("count-per")},
188
+ execute: async (m, ctx) => {
189
+ const countModel = m[1].charAt(0).toUpperCase() + m[1].slice(1);
190
+ const groupModel = m[2].charAt(0).toUpperCase() + m[2].slice(1);
191
+ const groupFk = groupModel.charAt(0).toLowerCase() + groupModel.slice(1) + 'Id';
192
+ const allResult = await ctx.store.query(countModel, {});
193
+ const counts: Record<string, number> = {};
194
+ for (const entity of allResult.entities) {
195
+ const groupKey = entity.data?.[groupFk] || 'unknown';
196
+ counts[groupKey] = (counts[groupKey] || 0) + 1;
197
+ }
198
+ return counts;
199
+ },
200
+ },`;
201
+ }
202
+ function generateCreate() {
203
+ return ` {
204
+ name: 'create',
205
+ pattern: ${p("create")},
206
+ execute: async (m, ctx) => {
207
+ const model = m[1];
208
+ const data = ctx.params.data || ctx.params;
209
+ return await ctx.store.create(model, data);
210
+ },
211
+ },`;
212
+ }
213
+ function generateUpdateField() {
214
+ return ` {
215
+ name: 'update-field',
216
+ pattern: ${p("update-field")},
217
+ execute: async (m, ctx) => {
218
+ const model = m[1];
219
+ const field = m[2];
220
+ let value: any = m[3].trim();
221
+ if (value === 'current time' || value === 'now') value = new Date().toISOString();
222
+ const id = resolveId(model, ctx);
223
+ if (!id) throw new Error(\`No id to update \${model}.\${field}\`);
224
+ // Check if field is a lifecycle \u2014 use evolveLifecycle (no data validation)
225
+ const entity = await ctx.store.retrieve(model, id);
226
+ if (entity?.metadata?.lifecycleStates && field in entity.metadata.lifecycleStates) {
227
+ return await ctx.store.evolveLifecycle(model, id, field, value);
228
+ }
229
+ return await ctx.store.update(model, id, { [field]: value });
230
+ },
231
+ },`;
232
+ }
233
+ function generateUpdate() {
234
+ return ` {
235
+ name: 'update',
236
+ pattern: ${p("update")},
237
+ execute: async (m, ctx) => {
238
+ const model = m[1];
239
+ const id = resolveId(model, ctx);
240
+ const data = ctx.params.data || ctx.params;
241
+ if (!id) throw new Error(\`No id to update \${model}\`);
242
+ return await ctx.store.update(model, id, data);
243
+ },
244
+ },`;
245
+ }
246
+ function generateDelete() {
247
+ return ` {
248
+ name: 'delete',
249
+ pattern: ${p("delete")},
250
+ execute: async (m, ctx) => {
251
+ const model = m[1];
252
+ const id = resolveId(model, ctx);
253
+ if (!id) throw new Error(\`No id to delete \${model}\`);
254
+ await ctx.store.delete(model, id);
255
+ return { deleted: true };
256
+ },
257
+ },`;
258
+ }
259
+ function generateTransition() {
260
+ return ` {
261
+ name: 'transition',
262
+ pattern: ${p("transition")},
263
+ execute: async (m, ctx) => {
264
+ const model = m[1];
265
+ const state = m[2];
266
+ const id = resolveId(model, ctx);
267
+ if (!id) throw new Error(\`No id to transition \${model}\`);
268
+ const entity = await ctx.store.retrieve(model, id);
269
+ if (!entity) throw new Error(\`\${model} not found for transition\`);
270
+ const lifecycleName = Object.keys(entity.metadata?.lifecycleStates || {})[0];
271
+ if (lifecycleName) {
272
+ return await ctx.store.evolveLifecycle(model, id, lifecycleName, state);
273
+ }
274
+ throw new Error(\`No lifecycle found on \${model} to transition\`);
275
+ },
276
+ },`;
277
+ }
278
+ function generateSet() {
279
+ return ` {
280
+ name: 'set',
281
+ pattern: ${p("set")},
282
+ execute: async (m, ctx) => {
283
+ const field = m[1];
284
+ let value: any = m[2].trim();
285
+ if (/current time|now|timestamp/i.test(value)) value = new Date().toISOString();
286
+ const id = resolveId(ctx.modelName, ctx);
287
+ if (!id) throw new Error(\`No id to set \${field}\`);
288
+ return await ctx.store.update(ctx.modelName, id, { [field]: value });
289
+ },
290
+ },`;
291
+ }
292
+ function generateIncrement() {
293
+ return ` {
294
+ name: 'increment',
295
+ pattern: ${p("increment")},
296
+ execute: async (m, ctx) => {
297
+ const field = m[1];
298
+ const amount = /^\\d+$/.test(m[2]) ? parseInt(m[2]) : (ctx.params[m[2]] || 1);
299
+ const id = resolveId(ctx.modelName, ctx);
300
+ if (!id) throw new Error(\`No id to increment \${field}\`);
301
+ const entity = await ctx.store.retrieve(ctx.modelName, id);
302
+ if (!entity) throw new Error(\`Entity not found to increment \${field}\`);
303
+ const current = (entity.data[field] || 0) as number;
304
+ return await ctx.store.update(ctx.modelName, id, { [field]: current + amount });
305
+ },
306
+ },`;
307
+ }
308
+ function generateDecrement() {
309
+ return ` {
310
+ name: 'decrement',
311
+ pattern: ${p("decrement")},
312
+ execute: async (m, ctx) => {
313
+ const field = m[1];
314
+ const amount = /^\\d+$/.test(m[2]) ? parseInt(m[2]) : (ctx.params[m[2]] || 1);
315
+ const id = resolveId(ctx.modelName, ctx);
316
+ if (!id) throw new Error(\`No id to decrement \${field}\`);
317
+ const entity = await ctx.store.retrieve(ctx.modelName, id);
318
+ if (!entity) throw new Error(\`Entity not found to decrement \${field}\`);
319
+ const current = (entity.data[field] || 0) as number;
320
+ if (current < amount) throw new Error(\`Cannot decrement \${field} below zero\`);
321
+ return await ctx.store.update(ctx.modelName, id, { [field]: current - amount });
322
+ },
323
+ },`;
324
+ }
325
+ function generateCalculate() {
326
+ return ` {
327
+ name: 'calculate',
328
+ pattern: ${p("calculate")},
329
+ execute: async (m, ctx) => {
330
+ const metric = m[1].toLowerCase();
331
+ if (metric.includes('percentage') || metric.includes('percent')) {
332
+ const counts = ctx.params._lastResult;
333
+ if (counts && typeof counts === 'object') {
334
+ const total = Object.values(counts as Record<string, number>).reduce((a: number, b: number) => a + b, 0);
335
+ if (total > 0) {
336
+ const pcts: Record<string, string> = {};
337
+ for (const [key, count] of Object.entries(counts as Record<string, number>)) {
338
+ pcts[key] = \`\${((count / total) * 100).toFixed(1)}%\`;
339
+ }
340
+ return pcts;
341
+ }
342
+ }
343
+ }
344
+ if (metric.includes('winner') || metric.includes('max') || metric.includes('highest')) {
345
+ const counts = ctx.params._lastResult;
346
+ if (counts && typeof counts === 'object') {
347
+ let maxKey = '', maxVal = -1;
348
+ for (const [key, val] of Object.entries(counts as Record<string, number>)) {
349
+ if (val > maxVal) { maxVal = val; maxKey = key; }
350
+ }
351
+ return { winner: maxKey, count: maxVal };
352
+ }
353
+ }
354
+ return 0;
355
+ },
356
+ },`;
357
+ }
358
+ function generateSendEvent() {
359
+ return ` {
360
+ name: 'send-event',
361
+ pattern: ${p("send-event")},
362
+ execute: async (m, ctx) => {
363
+ const event = m[1];
364
+ await ctx.eventBus.publish(event, {
365
+ modelName: ctx.modelName,
366
+ entityId: ctx.entity?.id || ctx.params.id,
367
+ operation: ctx.operationName,
368
+ timestamp: new Date().toISOString(),
369
+ });
370
+ return { event, published: true };
371
+ },
372
+ },`;
373
+ }
374
+ function generateSendNotification() {
375
+ return ` {
376
+ name: 'send-notification',
377
+ pattern: ${p("send-notification")},
378
+ execute: async (m, ctx) => {
379
+ const type = m[1];
380
+ await ctx.eventBus.publish(\`\${type}Notification\`, {
381
+ modelName: ctx.modelName,
382
+ entityId: ctx.entity?.id || ctx.params.id,
383
+ operation: ctx.operationName,
384
+ });
385
+ return { notification: type, sent: true };
386
+ },
387
+ },`;
388
+ }
389
+ function generateCallService() {
390
+ return ` {
391
+ name: 'call-service',
392
+ pattern: ${p("call-service")},
393
+ execute: async (m, ctx) => {
394
+ const service = m[1];
395
+ const method = m[2];
396
+ await ctx.eventBus.publish(\`\${service}.\${method}\`, ctx.params);
397
+ return { delegated: true, service, method };
398
+ },
399
+ },`;
400
+ }
401
+ function generateReturn() {
402
+ return ` {
403
+ name: 'return',
404
+ pattern: ${p("return")},
405
+ execute: async (m, ctx) => {
406
+ // Return the last computed result if available, otherwise entity or literal
407
+ if (ctx.params._lastResult !== undefined) return ctx.params._lastResult;
408
+ if (ctx.entity) return ctx.entity;
409
+ return { value: m[1].trim() };
410
+ },
411
+ },`;
412
+ }
413
+ export {
414
+ generateInterpreterModule
415
+ };
@@ -0,0 +1,177 @@
1
+ import { matchStep } from "./step-conventions.js";
2
+ function generateBehaviorBody(behavior, opMeta, context) {
3
+ const result = generateBehaviorWithHelpers(behavior, opMeta, context);
4
+ return result.body;
5
+ }
6
+ function generateBehaviorWithHelpers(behavior, opMeta, context) {
7
+ const parts = [];
8
+ const helperMethods = [];
9
+ const preconditions = generatePreconditionChecks(
10
+ behavior.preconditions || [],
11
+ context
12
+ );
13
+ if (preconditions) parts.push(preconditions);
14
+ const { code, helpers } = generateStepLogic(
15
+ behavior.steps || [],
16
+ context
17
+ );
18
+ parts.push(code);
19
+ helperMethods.push(...helpers);
20
+ const postconditions = generatePostconditionVerification(
21
+ behavior.postconditions || []
22
+ );
23
+ if (postconditions) parts.push(postconditions);
24
+ const events = generateEventPublishing(
25
+ behavior.sideEffects || [],
26
+ context.operationName
27
+ );
28
+ if (events) parts.push(events);
29
+ let body = parts.join("\n\n");
30
+ if (behavior.transactional) {
31
+ body = generateTransactionWrapper(body, context);
32
+ }
33
+ return { body, helperMethods };
34
+ }
35
+ function generatePreconditionChecks(preconditions, context) {
36
+ if (preconditions.length === 0) return "";
37
+ const checks = preconditions.map((pc) => matchPreconditionPattern(pc, context));
38
+ return ` // === PRECONDITIONS ===
39
+ ${checks.join("\n")}`;
40
+ }
41
+ function matchPreconditionPattern(precondition, context) {
42
+ const pc = precondition.toLowerCase();
43
+ const prismaModel = context.prismaModel || context.modelName;
44
+ const existsMatch = precondition.match(/^(\w+)\s+exists/i);
45
+ if (existsMatch) {
46
+ const entity = existsMatch[1];
47
+ const entityVar = entity.charAt(0).toLowerCase() + entity.slice(1);
48
+ return ` // Guard: ${precondition}
49
+ const ${entityVar} = await prisma.${entityVar}.findUnique({ where: { id: params.id } });
50
+ if (!${entityVar}) {
51
+ throw new Error('Precondition failed: ${precondition}');
52
+ }`;
53
+ }
54
+ if (pc.includes("is not empty") || pc.includes("is required")) {
55
+ const fieldMatch = precondition.match(/^(\w+)\s+is/i);
56
+ if (fieldMatch) {
57
+ const field = fieldMatch[1];
58
+ return ` // Guard: ${precondition}
59
+ if (!params.${field}) {
60
+ throw new Error('Precondition failed: ${precondition}');
61
+ }`;
62
+ }
63
+ }
64
+ if (pc.includes("is valid")) {
65
+ return ` // Guard: ${precondition}
66
+ const validation = this.validate(params, { operation: '${context.operationName}' });
67
+ if (!validation.valid) {
68
+ throw new Error('Precondition failed: ${precondition} \u2014 ' + validation.errors.join(', '));
69
+ }`;
70
+ }
71
+ const matchesMatch = precondition.match(/^(\w+)\s+(?:matches|equals)\s+(.+)/i);
72
+ if (matchesMatch) {
73
+ const left = matchesMatch[1];
74
+ const right = matchesMatch[2];
75
+ return ` // Guard: ${precondition}
76
+ if (params.${left.charAt(0).toLowerCase() + left.slice(1)} !== params.${right.charAt(0).toLowerCase() + right.slice(1)}) {
77
+ throw new Error('Precondition failed: ${precondition}');
78
+ }`;
79
+ }
80
+ const stateMatch = precondition.match(/^(\w+)\s+is\s+(\w+)$/i);
81
+ if (stateMatch) {
82
+ const model = stateMatch[1];
83
+ const state = stateMatch[2];
84
+ const modelVar = model.charAt(0).toLowerCase() + model.slice(1);
85
+ return ` // Guard: ${precondition}
86
+ const ${modelVar}State = await prisma.${modelVar}.findUniqueOrThrow({ where: { id: params.id } });
87
+ if (${modelVar}State.status !== '${state}') {
88
+ throw new Error('Precondition failed: ${precondition} (current: ' + ${modelVar}State.status + ')');
89
+ }`;
90
+ }
91
+ return ` // Guard: ${precondition}
92
+ // TODO: Implement precondition check`;
93
+ }
94
+ function generateStepLogic(steps, context) {
95
+ const helpers = [];
96
+ if (steps && steps.length > 0) {
97
+ const stepCode = steps.map((step, i) => {
98
+ if (typeof step !== "string") {
99
+ return ` // Step ${i + 1}: Complex operation \u2014 see expanded definition`;
100
+ }
101
+ const ctx = {
102
+ modelName: context.modelName,
103
+ prismaModel: context.prismaModel || context.modelName,
104
+ serviceName: context.serviceName,
105
+ operationName: context.operationName,
106
+ stepNum: i + 1
107
+ };
108
+ const result = matchStep(step, ctx);
109
+ if (result.helperMethod) {
110
+ helpers.push(result.helperMethod);
111
+ }
112
+ return result.call;
113
+ });
114
+ return {
115
+ code: ` // === EXECUTE ===
116
+ ${stepCode.join("\n\n")}`,
117
+ helpers
118
+ };
119
+ }
120
+ return {
121
+ code: ` // === EXECUTE ===
122
+ ${inferLogicFromOperationName(context)}`,
123
+ helpers
124
+ };
125
+ }
126
+ function inferLogicFromOperationName(context) {
127
+ const op = context.operationName;
128
+ const prismaModel = context.prismaModel || context.modelName;
129
+ const modelVar = prismaModel.charAt(0).toLowerCase() + prismaModel.slice(1);
130
+ if (op.startsWith("handle")) {
131
+ const event = op.replace("handle", "");
132
+ return ` // Event handler: ${op}
133
+ console.log('[${context.serviceName}] Processing ${event}', params);
134
+ return { handled: true, event: '${event}' };`;
135
+ }
136
+ if (op.startsWith("validate")) {
137
+ return ` // Validation: ${op}
138
+ const records = await prisma.${modelVar}.findMany({ where: { id: params.id } });
139
+ return { valid: records.length > 0, checked: records.length };`;
140
+ }
141
+ if (op.startsWith("get") || op.startsWith("list") || op.startsWith("find")) {
142
+ return ` return await prisma.${modelVar}.findMany({});`;
143
+ }
144
+ return ` // TODO: Implement ${op}
145
+ return { success: true };`;
146
+ }
147
+ function generatePostconditionVerification(postconditions) {
148
+ if (postconditions.length === 0) return "";
149
+ const checks = postconditions.map(
150
+ (pc) => ` console.assert(true, 'POSTCONDITION: ${pc}');`
151
+ );
152
+ return ` // === POSTCONDITIONS (dev-mode) ===
153
+ if (process.env.NODE_ENV === 'development') {
154
+ ${checks.join("\n")}
155
+ }`;
156
+ }
157
+ function generateEventPublishing(sideEffects, operationName) {
158
+ if (!sideEffects || sideEffects.length === 0) return "";
159
+ const publishes = sideEffects.map(
160
+ (event) => ` this.emit('${event}', { operation: '${operationName}', timestamp: new Date().toISOString() });`
161
+ );
162
+ return ` // === EVENTS ===
163
+ ${publishes.join("\n")}`;
164
+ }
165
+ function generateTransactionWrapper(body, context) {
166
+ return ` return await prisma.$transaction(async (tx) => {
167
+ ${body}
168
+ });`;
169
+ }
170
+ export {
171
+ generateBehaviorBody,
172
+ generateBehaviorWithHelpers,
173
+ generateEventPublishing,
174
+ generatePostconditionVerification,
175
+ generatePreconditionChecks,
176
+ generateTransactionWrapper
177
+ };