@specverse/engines 4.1.13 → 4.1.14

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 (34) hide show
  1. package/assets/prompts/core/standard/v9/behavior.prompt.yaml +120 -0
  2. package/dist/ai/behavior-ai-service.d.ts +63 -0
  3. package/dist/ai/behavior-ai-service.d.ts.map +1 -0
  4. package/dist/ai/behavior-ai-service.js +203 -0
  5. package/dist/ai/behavior-ai-service.js.map +1 -0
  6. package/dist/ai/index.d.ts +27 -0
  7. package/dist/ai/index.d.ts.map +1 -1
  8. package/dist/ai/index.js +30 -0
  9. package/dist/ai/index.js.map +1 -1
  10. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +16 -0
  11. package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +31 -30
  12. package/dist/libs/instance-factories/communication/templates/eventemitter/types-generator.js +79 -0
  13. package/dist/libs/instance-factories/communication/templates/eventemitter/websocket-bridge-generator.js +96 -0
  14. package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +25 -9
  15. package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +20 -2
  16. package/dist/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.js +141 -0
  17. package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +62 -42
  18. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +39 -7
  19. package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +101 -84
  20. package/dist/realize/index.d.ts.map +1 -1
  21. package/dist/realize/index.js +54 -0
  22. package/dist/realize/index.js.map +1 -1
  23. package/libs/instance-factories/cli/templates/commander/command-generator.ts +16 -0
  24. package/libs/instance-factories/communication/event-emitter.yaml +16 -12
  25. package/libs/instance-factories/communication/templates/eventemitter/bus-generator.ts +33 -35
  26. package/libs/instance-factories/communication/templates/eventemitter/types-generator.ts +95 -0
  27. package/libs/instance-factories/communication/templates/eventemitter/websocket-bridge-generator.ts +105 -0
  28. package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +32 -11
  29. package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +23 -2
  30. package/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.ts +211 -0
  31. package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +86 -40
  32. package/libs/instance-factories/services/templates/prisma/controller-generator.ts +54 -8
  33. package/libs/instance-factories/services/templates/prisma/step-conventions.ts +166 -85
  34. package/package.json +1 -1
@@ -6,14 +6,17 @@ function generateBehaviorBody(behavior, opMeta, context) {
6
6
  function generateBehaviorWithHelpers(behavior, opMeta, context) {
7
7
  const parts = [];
8
8
  const helperMethods = [];
9
+ const preconditionDeclared = /* @__PURE__ */ new Set();
9
10
  const preconditions = generatePreconditionChecks(
10
11
  behavior.preconditions || [],
11
- context
12
+ context,
13
+ preconditionDeclared
12
14
  );
13
15
  if (preconditions) parts.push(preconditions);
14
- const { code, helpers } = generateStepLogic(
16
+ const { code, helpers, unmatchedSteps } = generateStepLogic(
15
17
  behavior.steps || [],
16
- context
18
+ context,
19
+ preconditionDeclared
17
20
  );
18
21
  parts.push(code);
19
22
  helperMethods.push(...helpers);
@@ -23,76 +26,80 @@ function generateBehaviorWithHelpers(behavior, opMeta, context) {
23
26
  if (postconditions) parts.push(postconditions);
24
27
  const events = generateEventPublishing(
25
28
  behavior.sideEffects || [],
26
- context.operationName
29
+ context.operationName,
30
+ context.parameterNames
27
31
  );
28
32
  if (events) parts.push(events);
29
33
  let body = parts.join("\n\n");
30
34
  if (behavior.transactional) {
31
35
  body = generateTransactionWrapper(body, context);
32
36
  }
33
- return { body, helperMethods };
37
+ return { body, helperMethods, unmatchedSteps };
34
38
  }
35
- function generatePreconditionChecks(preconditions, context) {
39
+ function generatePreconditionChecks(preconditions, context, declared) {
36
40
  if (preconditions.length === 0) return "";
37
- const checks = preconditions.map((pc) => matchPreconditionPattern(pc, context));
41
+ if (!declared) declared = /* @__PURE__ */ new Set();
42
+ const checks = preconditions.map((pc) => matchPreconditionPattern(pc, context, declared));
38
43
  return ` // === PRECONDITIONS ===
39
44
  ${checks.join("\n")}`;
40
45
  }
41
- function matchPreconditionPattern(precondition, context) {
46
+ function findIdParam(modelName, paramNames) {
47
+ const modelVar = modelName.charAt(0).toLowerCase() + modelName.slice(1);
48
+ const idParam = paramNames.find((p) => p === `${modelVar}Id`);
49
+ if (idParam) return idParam;
50
+ if (paramNames.includes("id")) return "id";
51
+ return `${modelVar}Id`;
52
+ }
53
+ function matchPreconditionPattern(precondition, context, declared) {
42
54
  const pc = precondition.toLowerCase();
43
- const prismaModel = context.prismaModel || context.modelName;
55
+ const params = context.parameterNames || [];
44
56
  const existsMatch = precondition.match(/^(\w+)\s+exists/i);
45
57
  if (existsMatch) {
46
58
  const entity = existsMatch[1];
47
59
  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
- }`;
60
+ const idParam = findIdParam(entity, params);
61
+ if (declared.has(entityVar)) {
62
+ return ` // Guard: ${precondition} (already fetched)`;
63
+ }
64
+ declared.add(entityVar);
65
+ return ` const ${entityVar} = await prisma.${entityVar}.findUniqueOrThrow({ where: { id: ${idParam} } });`;
53
66
  }
54
67
  if (pc.includes("is not empty") || pc.includes("is required")) {
55
68
  const fieldMatch = precondition.match(/^(\w+)\s+is/i);
56
69
  if (fieldMatch) {
57
70
  const field = fieldMatch[1];
58
- return ` // Guard: ${precondition}
59
- if (!params.${field}) {
60
- throw new Error('Precondition failed: ${precondition}');
61
- }`;
71
+ const paramRef = params.includes(field) ? field : `params.${field}`;
72
+ return ` if (!${paramRef}) throw new Error('${field} is required');`;
62
73
  }
63
74
  }
64
75
  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
- }`;
76
+ return ` // TODO: Implement validation: ${precondition}`;
70
77
  }
71
78
  const matchesMatch = precondition.match(/^(\w+)\s+(?:matches|equals)\s+(.+)/i);
72
79
  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
- }`;
80
+ const left = matchesMatch[1].charAt(0).toLowerCase() + matchesMatch[1].slice(1);
81
+ const right = matchesMatch[2].charAt(0).toLowerCase() + matchesMatch[2].slice(1);
82
+ return ` if (${left} !== ${right}) throw new Error('${matchesMatch[1]} must match ${matchesMatch[2]}');`;
79
83
  }
80
84
  const stateMatch = precondition.match(/^(\w+)\s+is\s+(\w+)$/i);
81
85
  if (stateMatch) {
82
86
  const model = stateMatch[1];
83
87
  const state = stateMatch[2];
84
88
  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
- }`;
89
+ const idParam = findIdParam(model, params);
90
+ if (!declared.has(modelVar)) {
91
+ declared.add(modelVar);
92
+ return ` const ${modelVar} = await prisma.${modelVar}.findUniqueOrThrow({ where: { id: ${idParam} } });
93
+ if (${modelVar}.status !== '${state}') throw new Error('${model} must be ${state}, got ' + ${modelVar}.status);`;
94
+ }
95
+ return ` if (${modelVar}.status !== '${state}') throw new Error('${model} must be ${state}, got ' + ${modelVar}.status);`;
90
96
  }
91
- return ` // Guard: ${precondition}
92
- // TODO: Implement precondition check`;
97
+ return ` // TODO: Implement precondition: ${precondition}`;
93
98
  }
94
- function generateStepLogic(steps, context) {
99
+ function generateStepLogic(steps, context, preconditionDeclared) {
95
100
  const helpers = [];
101
+ const unmatchedSteps = [];
102
+ const declaredVars = preconditionDeclared || /* @__PURE__ */ new Set();
96
103
  if (steps && steps.length > 0) {
97
104
  const stepCode = steps.map((step, i) => {
98
105
  if (typeof step !== "string") {
@@ -103,24 +110,36 @@ function generateStepLogic(steps, context) {
103
110
  prismaModel: context.prismaModel || context.modelName,
104
111
  serviceName: context.serviceName,
105
112
  operationName: context.operationName,
106
- stepNum: i + 1
113
+ stepNum: i + 1,
114
+ parameterNames: context.parameterNames,
115
+ declaredVars
107
116
  };
108
117
  const result = matchStep(step, ctx);
109
118
  if (result.helperMethod) {
110
119
  helpers.push(result.helperMethod);
111
120
  }
121
+ if (!result.matched && result.functionName) {
122
+ unmatchedSteps.push({
123
+ step,
124
+ functionName: result.functionName,
125
+ operationName: context.operationName,
126
+ inputs: result.inputs || []
127
+ });
128
+ }
112
129
  return result.call;
113
130
  });
114
131
  return {
115
132
  code: ` // === EXECUTE ===
116
133
  ${stepCode.join("\n\n")}`,
117
- helpers
134
+ helpers,
135
+ unmatchedSteps
118
136
  };
119
137
  }
120
138
  return {
121
139
  code: ` // === EXECUTE ===
122
140
  ${inferLogicFromOperationName(context)}`,
123
- helpers
141
+ helpers,
142
+ unmatchedSteps
124
143
  };
125
144
  }
126
145
  function inferLogicFromOperationName(context) {
@@ -154,10 +173,11 @@ function generatePostconditionVerification(postconditions) {
154
173
  ${checks.join("\n")}
155
174
  }`;
156
175
  }
157
- function generateEventPublishing(sideEffects, operationName) {
176
+ function generateEventPublishing(sideEffects, operationName, parameterNames) {
158
177
  if (!sideEffects || sideEffects.length === 0) return "";
178
+ const paramFields = parameterNames?.length ? parameterNames.join(", ") + ", " : "";
159
179
  const publishes = sideEffects.map(
160
- (event) => ` this.emit('${event}', { operation: '${operationName}', timestamp: new Date().toISOString() });`
180
+ (event) => ` await eventBus.publish('${event}', { ${paramFields}timestamp: new Date().toISOString() });`
161
181
  );
162
182
  return ` // === EVENTS ===
163
183
  ${publishes.join("\n")}`;
@@ -1,4 +1,5 @@
1
1
  import { buildTransitionMap, isAutoField } from "@specverse/types/spec-rules";
2
+ import { generateBehaviorWithHelpers } from "./behavior-generator.js";
2
3
  function generatePrismaController(context) {
3
4
  const { controller, model, spec, models: allModels } = context;
4
5
  if (!controller) {
@@ -16,6 +17,7 @@ function generatePrismaController(context) {
16
17
  const idAttr = (Array.isArray(model.attributes) ? model.attributes : Object.values(model.attributes || {})).find((a) => a.name === "id");
17
18
  const idType = idAttr?.type || "UUID";
18
19
  const needsIntParse = idType === "Integer" || idType === "Int" || idType === "Number";
20
+ const customActions = generateCustomActions(controller, modelName, modelVar);
19
21
  return `/**
20
22
  * ${controllerName}
21
23
  * Model-specific business logic for ${modelName}
@@ -23,7 +25,8 @@ function generatePrismaController(context) {
23
25
  */
24
26
 
25
27
  import { PrismaClient } from '@prisma/client';
26
- ${hasEventPublishing(curedOps, controller) ? `import { eventBus, EventName } from '../events/eventBus.js';` : ""}
28
+ ${hasEventPublishing(curedOps, controller) ? `import { eventBus } from '../events/eventBus.js';` : ""}
29
+ ${customActions.needsAiBehaviors ? `import * as aiBehaviors from '../behaviors/${modelName}Controller.ai.js';` : ""}
27
30
 
28
31
  const prisma = new PrismaClient();
29
32
 
@@ -42,7 +45,7 @@ export class ${controllerName} {
42
45
  ${curedOps.update ? generateUpdateMethod(model, modelName, modelVar, controller, allModels) : ""}
43
46
  ${curedOps.evolve ? generateEvolveMethod(model, modelName, modelVar, controller) : ""}
44
47
  ${curedOps.delete ? generateDeleteMethod(model, modelName, modelVar, controller) : ""}
45
- ${generateCustomActions(controller, modelName, modelVar)}
48
+ ${customActions.code}
46
49
  }
47
50
 
48
51
  // Export singleton instance
@@ -298,21 +301,44 @@ function generateDeleteMethod(model, modelName, modelVar, controller) {
298
301
  }
299
302
  function generateCustomActions(controller, modelName, modelVar) {
300
303
  if (!controller.actions || Object.keys(controller.actions).length === 0) {
301
- return "";
304
+ return { code: "", unmatchedSteps: [], needsAiBehaviors: false };
302
305
  }
303
306
  const actions = [];
307
+ const allUnmatchedSteps = [];
304
308
  Object.entries(controller.actions).forEach(([actionName, action]) => {
309
+ const behavior = {
310
+ preconditions: action.requires || action.preconditions || [],
311
+ steps: action.steps || [],
312
+ postconditions: action.ensures || action.postconditions || [],
313
+ sideEffects: action.publishes || action.events || [],
314
+ transactional: action.transactional
315
+ };
316
+ const ctx = {
317
+ modelName,
318
+ serviceName: `${modelName}Controller`,
319
+ operationName: actionName,
320
+ prismaModel: modelVar,
321
+ parameterNames: Object.keys(action.parameters || {})
322
+ };
323
+ const result = generateBehaviorWithHelpers(behavior, {}, ctx);
324
+ allUnmatchedSteps.push(...result.unmatchedSteps);
305
325
  actions.push(`
306
326
  /**
307
327
  * ${actionName}
308
328
  * ${action.description || ""}
309
329
  */
310
330
  public async ${actionName}(${generateActionParams(action)}): Promise<any> {
311
- // TODO: Implement ${actionName} logic
312
- throw new Error('${actionName} not implemented');
331
+ ${result.body}
313
332
  }`);
333
+ if (result.helperMethods.length > 0) {
334
+ actions.push(...result.helperMethods);
335
+ }
314
336
  });
315
- return actions.join("\n");
337
+ return {
338
+ code: actions.join("\n"),
339
+ unmatchedSteps: allUnmatchedSteps,
340
+ needsAiBehaviors: allUnmatchedSteps.length > 0
341
+ };
316
342
  }
317
343
  function generateActionParams(action) {
318
344
  if (!action.parameters || Object.keys(action.parameters).length === 0) {
@@ -391,7 +417,13 @@ ${includes}
391
417
  }`;
392
418
  }
393
419
  function hasEventPublishing(curedOps, controller) {
394
- return controller.publishes && Array.isArray(controller.publishes) && controller.publishes.length > 0;
420
+ if (controller.publishes && Array.isArray(controller.publishes) && controller.publishes.length > 0) return true;
421
+ if (controller.actions) {
422
+ for (const action of Object.values(controller.actions)) {
423
+ if (action.publishes?.length > 0 || action.events?.length > 0 || action.sideEffects?.length > 0) return true;
424
+ }
425
+ }
426
+ return false;
395
427
  }
396
428
  export {
397
429
  generatePrismaController as default
@@ -4,15 +4,47 @@ function toVar(name) {
4
4
  function toMethod(words) {
5
5
  return words.trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toLowerCase());
6
6
  }
7
+ function resolveValue(rawValue, ctx) {
8
+ const value = rawValue.trim().replace(/^['"]|['"]$/g, "");
9
+ if (/^(current\s*time|now|timestamp)$/i.test(value)) {
10
+ return "new Date().toISOString()";
11
+ }
12
+ if (value === "true" || value === "false") return value;
13
+ if (/^-?\d+(\.\d+)?$/.test(value)) return value;
14
+ const declared = ctx.declaredVars || /* @__PURE__ */ new Set();
15
+ const params = ctx.parameterNames || [];
16
+ const rootMatch = value.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(\.[a-zA-Z0-9_.]+)?$/);
17
+ if (rootMatch) {
18
+ const root = rootMatch[1];
19
+ if (declared.has(root) || params.includes(root)) {
20
+ return value;
21
+ }
22
+ }
23
+ const tokens = value.toLowerCase().split(/\s+/);
24
+ for (const declaredName of [...declared, ...params]) {
25
+ const normalized = declaredName.toLowerCase();
26
+ if (tokens.includes(normalized)) return declaredName;
27
+ }
28
+ const calcMatch = value.match(/^(?:the\s+|calculated\s+|computed\s+|resulting\s+)(\w+)/i);
29
+ if (calcMatch) {
30
+ const field = calcMatch[1];
31
+ const stepResults = [...declared].filter((v) => /^step\d+Result$/.test(v)).sort().reverse();
32
+ if (stepResults.length > 0) {
33
+ return `(${stepResults[0]} as any).${field} /* TODO: verify field name */`;
34
+ }
35
+ }
36
+ if (/\s/.test(value)) {
37
+ return `/* TODO: resolve "${value}" \u2014 no matching declared variable */ null`;
38
+ }
39
+ return `'${value.replace(/'/g, "\\'")}'`;
40
+ }
7
41
  const STEP_CONVENTIONS = [
8
42
  // --- Validation ---
9
43
  {
10
44
  name: "validate",
11
45
  pattern: /^validate\s+(.+)/i,
12
46
  generateCall: (m, ctx) => ` // Step ${ctx.stepNum}: Validate ${m[1]}
13
- const validationResult = this.validate(params, { operation: '${ctx.operationName}' });
14
- if (!validationResult.valid) {
15
- throw new Error(\`Validation failed: \${validationResult.errors.join(', ')}\`);
47
+ // TODO: Add validation logic for ${m[1]}
16
48
  }`
17
49
  },
18
50
  // --- Check / Guard ---
@@ -23,21 +55,9 @@ const STEP_CONVENTIONS = [
23
55
  const condition = m[1];
24
56
  const methodName = toMethod("check " + condition);
25
57
  return ` // Step ${ctx.stepNum}: Check ${condition}
26
- const checkResult = await this.${methodName}(params);
27
- if (!checkResult) {
28
- throw new Error('Check failed: ${condition}');
29
- }`;
30
- },
31
- generateMethod: (m, ctx) => {
32
- const condition = m[1];
33
- const methodName = toMethod("check " + condition);
34
- return `
35
- private async ${methodName}(params: any): Promise<boolean> {
36
58
  // TODO: Implement check \u2014 ${condition}
37
- const ${toVar(ctx.prismaModel)} = await this.prisma.${toVar(ctx.prismaModel)}.findUnique({ where: { id: params.id } });
38
- if (!${toVar(ctx.prismaModel)}) return false;
39
- return true;
40
- }`;
59
+ // const checkResult = ...
60
+ // if (!checkResult) throw new Error('Check failed: ${condition}');`;
41
61
  }
42
62
  },
43
63
  // --- Find / Lookup ---
@@ -47,21 +67,29 @@ const STEP_CONVENTIONS = [
47
67
  generateCall: (m, ctx) => {
48
68
  const model = m[1];
49
69
  const field = m[2];
70
+ const modelVar = toVar(model);
71
+ const params = ctx.parameterNames || [];
72
+ const declared = ctx.declaredVars || /* @__PURE__ */ new Set();
73
+ const idParam = field === "id" ? params.find((p) => p === `${modelVar}Id`) || params.find((p) => p === "id") || `${modelVar}Id` : params.find((p) => p === field) || `params.${field}`;
74
+ if (declared.has(modelVar)) {
75
+ return ` // Step ${ctx.stepNum}: Find ${model} by ${field} (already loaded)`;
76
+ }
77
+ declared.add(modelVar);
50
78
  return ` // Step ${ctx.stepNum}: Find ${model} by ${field}
51
- const ${toVar(model)} = await this.prisma.${toVar(model)}.findUnique({ where: { ${field}: params.${field} } });
52
- if (!${toVar(model)}) {
53
- throw new Error('${model} not found by ${field}');
54
- }`;
79
+ const ${modelVar} = await prisma.${modelVar}.findUniqueOrThrow({ where: { ${field}: ${idParam} } });`;
55
80
  }
56
81
  },
57
82
  // --- Create ---
58
83
  {
59
84
  name: "create",
60
- pattern: /^create\s+(\w+)(?:\s+record)?/i,
85
+ pattern: /^create\s+(\w+)(?:\s+(?:with\s+)?(.+))?/i,
61
86
  generateCall: (m, ctx) => {
62
87
  const model = m[1];
88
+ const modelVar = toVar(model);
89
+ const paramNames = ctx.parameterNames || [];
90
+ const dataFields = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "data";
63
91
  return ` // Step ${ctx.stepNum}: Create ${model}
64
- const ${toVar(model)} = await this.prisma.${toVar(model)}.create({ data: params });`;
92
+ const ${modelVar} = await prisma.${modelVar}.create({ data: ${dataFields} });`;
65
93
  }
66
94
  },
67
95
  // --- Update field ---
@@ -71,12 +99,12 @@ const STEP_CONVENTIONS = [
71
99
  generateCall: (m, ctx) => {
72
100
  const model = m[1];
73
101
  const field = m[2];
74
- const value = m[3].trim().replace(/"/g, "'");
75
- const isString = /^[a-z]/.test(value) && !value.includes(".");
76
- const val = isString ? `'${value}'` : value;
77
- return ` // Step ${ctx.stepNum}: Update ${model} ${field} to ${value}
78
- await this.prisma.${toVar(model)}.update({
79
- where: { id: params.id },
102
+ const rawValue = m[3];
103
+ const modelVar = toVar(model);
104
+ const val = resolveValue(rawValue, ctx);
105
+ return ` // Step ${ctx.stepNum}: Update ${model} ${field} to ${rawValue.trim()}
106
+ await prisma.${modelVar}.update({
107
+ where: { id: ${modelVar}.id },
80
108
  data: { ${field}: ${val} },
81
109
  });`;
82
110
  }
@@ -87,9 +115,10 @@ const STEP_CONVENTIONS = [
87
115
  pattern: /^update\s+(\w+)(?:\s+(.+))?/i,
88
116
  generateCall: (m, ctx) => {
89
117
  const model = m[1];
118
+ const modelVar = toVar(model);
90
119
  return ` // Step ${ctx.stepNum}: Update ${model}
91
- await this.prisma.${toVar(model)}.update({
92
- where: { id: params.id },
120
+ await prisma.${modelVar}.update({
121
+ where: { id: ${modelVar}.id },
93
122
  data: params,
94
123
  });`;
95
124
  }
@@ -100,8 +129,9 @@ const STEP_CONVENTIONS = [
100
129
  pattern: /^delete\s+(\w+)/i,
101
130
  generateCall: (m, ctx) => {
102
131
  const model = m[1];
132
+ const modelVar = toVar(model);
103
133
  return ` // Step ${ctx.stepNum}: Delete ${model}
104
- await this.prisma.${toVar(model)}.delete({ where: { id: params.id } });`;
134
+ await prisma.${modelVar}.delete({ where: { id: ${modelVar}.id } });`;
105
135
  }
106
136
  },
107
137
  // --- Transition / Evolve ---
@@ -111,11 +141,11 @@ const STEP_CONVENTIONS = [
111
141
  generateCall: (m, ctx) => {
112
142
  const model = m[1];
113
143
  const state = m[2];
144
+ const modelVar = toVar(model);
114
145
  return ` // Step ${ctx.stepNum}: Transition ${model} to ${state}
115
- const current = await this.prisma.${toVar(model)}.findUniqueOrThrow({ where: { id: params.id } });
116
- // Lifecycle validation would check valid transition here
117
- await this.prisma.${toVar(model)}.update({
118
- where: { id: params.id },
146
+ if (${modelVar}.status === '${state}') throw new Error('${model} is already ${state}');
147
+ await prisma.${modelVar}.update({
148
+ where: { id: ${modelVar}.id },
119
149
  data: { status: '${state}' },
120
150
  });`;
121
151
  }
@@ -126,12 +156,12 @@ const STEP_CONVENTIONS = [
126
156
  pattern: /^set\s+(\w+)\s+to\s+(.+)/i,
127
157
  generateCall: (m, ctx) => {
128
158
  const field = m[1];
129
- const value = m[2].trim();
130
- const isCurrentTime = /current time|now|timestamp/i.test(value);
131
- const val = isCurrentTime ? "new Date()" : `'${value}'`;
132
- return ` // Step ${ctx.stepNum}: Set ${field} to ${value}
133
- await this.prisma.${toVar(ctx.prismaModel)}.update({
134
- where: { id: params.id },
159
+ const rawValue = m[2];
160
+ const modelVar = toVar(ctx.prismaModel);
161
+ const val = resolveValue(rawValue, ctx);
162
+ return ` // Step ${ctx.stepNum}: Set ${field} to ${rawValue.trim()}
163
+ await prisma.${modelVar}.update({
164
+ where: { id: ${modelVar}.id },
135
165
  data: { ${field}: ${val} },
136
166
  });`;
137
167
  }
@@ -143,10 +173,11 @@ const STEP_CONVENTIONS = [
143
173
  generateCall: (m, ctx) => {
144
174
  const field = m[1];
145
175
  const amount = m[2];
146
- const amountVal = /^\d+$/.test(amount) ? amount : `params.${amount}`;
176
+ const modelVar = toVar(ctx.prismaModel);
177
+ const amountVal = /^\d+$/.test(amount) ? amount : amount;
147
178
  return ` // Step ${ctx.stepNum}: Increment ${field} by ${amount}
148
- await this.prisma.${toVar(ctx.prismaModel)}.update({
149
- where: { id: params.id },
179
+ await prisma.${modelVar}.update({
180
+ where: { id: ${modelVar}.id },
150
181
  data: { ${field}: { increment: ${amountVal} } },
151
182
  });`;
152
183
  }
@@ -158,38 +189,18 @@ const STEP_CONVENTIONS = [
158
189
  generateCall: (m, ctx) => {
159
190
  const field = m[1];
160
191
  const amount = m[2];
161
- const amountVal = /^\d+$/.test(amount) ? amount : `params.${amount}`;
192
+ const modelVar = toVar(ctx.prismaModel);
193
+ const amountVal = /^\d+$/.test(amount) ? amount : amount;
162
194
  return ` // Step ${ctx.stepNum}: Decrement ${field} by ${amount}
163
- const current${field} = await this.prisma.${toVar(ctx.prismaModel)}.findUniqueOrThrow({ where: { id: params.id } });
164
- if ((current${field}.${field} || 0) < ${amountVal}) {
165
- throw new Error('Cannot decrement ${field} below zero');
166
- }
167
- await this.prisma.${toVar(ctx.prismaModel)}.update({
168
- where: { id: params.id },
195
+ await prisma.${modelVar}.update({
196
+ where: { id: ${modelVar}.id },
169
197
  data: { ${field}: { decrement: ${amountVal} } },
170
198
  });`;
171
199
  }
172
200
  },
173
- // --- Calculate ---
174
- {
175
- name: "calculate",
176
- pattern: /^calculate\s+(.+)/i,
177
- generateCall: (m, ctx) => {
178
- const metric = m[1];
179
- const methodName = toMethod("calculate " + metric);
180
- return ` // Step ${ctx.stepNum}: Calculate ${metric}
181
- const ${toVar(metric.replace(/\s+/g, ""))} = await this.${methodName}(params);`;
182
- },
183
- generateMethod: (m, ctx) => {
184
- const metric = m[1];
185
- const methodName = toMethod("calculate " + metric);
186
- return `
187
- private async ${methodName}(params: any): Promise<number> {
188
- // TODO: Implement calculation \u2014 ${metric}
189
- return 0;
190
- }`;
191
- }
192
- },
201
+ // NOTE: "calculate X" is intentionally NOT a convention — it falls through to AI
202
+ // behaviors because calculations are domain-specific pure functions that benefit
203
+ // from AI generation with the full input context.
193
204
  // --- Send event ---
194
205
  {
195
206
  name: "send-event",
@@ -197,7 +208,7 @@ const STEP_CONVENTIONS = [
197
208
  generateCall: (m, ctx) => {
198
209
  const event = m[1];
199
210
  return ` // Step ${ctx.stepNum}: Emit ${event} event
200
- this.emit('${event}', { ${toVar(ctx.prismaModel)}Id: params.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
211
+ await eventBus.publish('${event}', { ${toVar(ctx.prismaModel)}Id: ${toVar(ctx.prismaModel)}.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
201
212
  }
202
213
  },
203
214
  // --- Send notification ---
@@ -207,7 +218,7 @@ const STEP_CONVENTIONS = [
207
218
  generateCall: (m, ctx) => {
208
219
  const type = m[1];
209
220
  return ` // Step ${ctx.stepNum}: Send ${type} notification
210
- this.emit('${type}Notification', { ${toVar(ctx.prismaModel)}Id: params.id, operation: '${ctx.operationName}' });`;
221
+ await eventBus.publish('${type}Notification', { ${toVar(ctx.prismaModel)}Id: ${toVar(ctx.prismaModel)}.id, operation: '${ctx.operationName}' });`;
211
222
  }
212
223
  },
213
224
  // --- Call service ---
@@ -218,7 +229,7 @@ const STEP_CONVENTIONS = [
218
229
  const service = m[1];
219
230
  const method = m[2];
220
231
  return ` // Step ${ctx.stepNum}: Call ${service}.${method}
221
- await this.${toVar(service)}.${method}(params);`;
232
+ await ${toVar(service)}.${method}({ ${(ctx.parameterNames || []).join(", ")} });`;
222
233
  }
223
234
  },
224
235
  // --- Return ---
@@ -243,19 +254,25 @@ function matchStep(step, ctx) {
243
254
  if (match) {
244
255
  return {
245
256
  call: convention.generateCall(match, ctx),
246
- helperMethod: convention.generateMethod?.(match, ctx)
257
+ helperMethod: convention.generateMethod?.(match, ctx),
258
+ matched: true
247
259
  };
248
260
  }
249
261
  }
250
- const methodName = toMethod(step);
262
+ const functionName = toMethod(step);
263
+ const declared = Array.from(ctx.declaredVars || []);
264
+ const paramNames = ctx.parameterNames || [];
265
+ const inputs = [...paramNames, ...declared];
266
+ const resultVar = `step${ctx.stepNum}Result`;
267
+ const inputObj = inputs.length > 0 ? `{ ${inputs.join(", ")} }` : "{}";
268
+ if (ctx.declaredVars) ctx.declaredVars.add(resultVar);
251
269
  return {
252
- call: ` // Step ${ctx.stepNum}: ${step}
253
- await this.${methodName}(params);`,
254
- helperMethod: `
255
- private async ${methodName}(params: any): Promise<void> {
256
- // TODO: Implement \u2014 ${step}
257
- throw new Error('Not implemented: ${methodName}');
258
- }`
270
+ call: ` // Step ${ctx.stepNum}: ${step} [AI-generated \u2014 pure function]
271
+ const ${resultVar} = await aiBehaviors.${functionName}(${inputObj});`,
272
+ matched: false,
273
+ functionName,
274
+ inputs,
275
+ resultVar
259
276
  };
260
277
  }
261
278
  export {
@@ -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;IAua9F;;;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"}
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;IA4d9F;;;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"}