@bobtail.software/b-durable 1.0.3 → 1.0.5

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.
@@ -1,53 +1,721 @@
1
1
  #!/usr/bin/env node
2
- import j from"path";import{existsSync as Z,mkdirSync as V,rmSync as ee}from"fs";import y from"path";import{Node as g,Project as te,SyntaxKind as $,VariableDeclarationKind as W}from"ts-morph";var ne="bDurable";async function K(e){console.log("Iniciando compilador de workflows duraderos...");let{inputDir:t,outputDir:n,packageName:o}=e,r=new te({tsConfigFilePath:y.resolve(process.cwd(),"tsconfig.json")}),a=r.addSourceFilesAtPaths(`${t}/**/*.ts`);Z(n)&&(console.log(`Limpiando directorio de salida: ${n}`),ee(n,{recursive:!0,force:!0})),V(n,{recursive:!0});let u=r.createDirectory(n);console.log(`Encontrados ${a.length} archivos de workflow para procesar.`);let i=[];for(let c of a){console.log(`
3
- Procesando archivo: ${c.getBaseName()}`);let s=c.getDescendantsOfKind($.CallExpression).filter(d=>d.getExpression().getText()===ne);if(s.length!==0)for(let d of s){let l=d.getParentIfKind($.VariableDeclaration);if(!l)continue;let x=l.getName();console.log(` -> Transformando workflow: ${x}`);let[p]=d.getArguments();if(!g.isArrowFunction(p))continue;let m=c.getBaseName().replace(/\.ts$/,".compiled.mts"),f=y.join(u.getPath(),m),T=r.createSourceFile(f,"",{overwrite:!0});oe(x,p,r,o,T),console.log(` -> Archivo generado: ${y.relative(process.cwd(),f)}`);let S=m;i.push({name:x,importPath:`./${S}`})}}if(i.length>0){let c=y.join(u.getPath(),"index.mts"),s=r.createSourceFile(c,"",{overwrite:!0});s.addStatements(`// Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.
4
- `),s.addImportDeclaration({isTypeOnly:!0,moduleSpecifier:o,namedImports:["DurableFunction"]});for(let l of i)s.addImportDeclaration({moduleSpecifier:l.importPath,namedImports:[l.name]});s.addExportDeclaration({namedExports:i.map(l=>l.name)}),s.addStatements(`
5
- `),s.addVariableStatement({declarationKind:W.Const,declarations:[{name:"durableFunctions",type:"Map<string, DurableFunction<any, any>>",initializer:"new Map()"}]});let d=i.map(l=>`durableFunctions.set(${l.name}.name, ${l.name});`);s.addStatements(d),s.addStatements(`
6
- `),s.addExportAssignment({isExportEquals:!1,expression:"durableFunctions"}),console.log(`
7
- -> Archivo de \xEDndice generado: ${y.basename(c)}`)}await r.save(),console.log(`
8
- Compilaci\xF3n completada exitosamente.`)}function oe(e,t,n,o,r){let a=t.getBody();if(!g.isBlock(a))throw new Error(`El cuerpo del workflow '${e}' debe ser un bloque {}.`);let u=new Map,{clauses:i}=F(a.getStatements(),0,u,n,o,""),[c]=t.getParameters(),s=t.getParameters()[1],l=s&&s.getName()==="context"?"const { log, workflowId } = context;":"",x=`Awaited<${t.getReturnType().getText()}>`,p=new Set,E="{}",m="unknown",f=t.getSourceFile();if(f.getInterfaces().forEach(S=>{p.add(S.getFullText())}),f.getTypeAliases().forEach(S=>{p.add(S.getFullText())}),c){E=c.getNameNode().getText();let S=c.getTypeNode();S&&(m=S.getText())}r.addImportDeclaration({isTypeOnly:!0,moduleSpecifier:o,namedImports:["DurableFunction","WorkflowContext","Instruction"]}),p.size>0&&(r.addStatements(`
9
- `),r.addStatements(Array.from(p))),r.addStatements(`
10
- // Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.
11
- `);let T=`{
2
+
3
+ // src/compiler/cli.ts
4
+ import path2 from "path";
5
+
6
+ // src/compiler/compile.ts
7
+ import { existsSync, mkdirSync, rmSync } from "fs";
8
+ import path from "path";
9
+ import * as prettier from "prettier";
10
+ import {
11
+ Node,
12
+ Project,
13
+ SyntaxKind,
14
+ ts,
15
+ VariableDeclarationKind
16
+ } from "ts-morph";
17
+ var DURABLE_WRAPPER_NAME = "bDurable";
18
+ var TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | ts.TypeFormatFlags.NoTruncation;
19
+ async function formatSourceFileWithPrettier(sourceFile) {
20
+ const filePath = sourceFile.getFilePath();
21
+ const unformattedText = sourceFile.getFullText();
22
+ const prettierConfig = await prettier.resolveConfig(filePath);
23
+ const formattedText = await prettier.format(unformattedText, {
24
+ ...prettierConfig,
25
+ parser: "typescript"
26
+ });
27
+ sourceFile.replaceWithText(formattedText);
28
+ }
29
+ async function compileWorkflows(options) {
30
+ console.log("Iniciando compilador de workflows duraderos...");
31
+ const { inputDir, outputDir, packageName } = options;
32
+ const project = new Project({
33
+ tsConfigFilePath: path.resolve(process.cwd(), "tsconfig.json")
34
+ });
35
+ const sourceFiles = project.addSourceFilesAtPaths(`${inputDir}/**/*.ts`);
36
+ if (existsSync(outputDir)) {
37
+ console.log(`Limpiando directorio de salida: ${outputDir}`);
38
+ rmSync(outputDir, { recursive: true, force: true });
39
+ }
40
+ mkdirSync(outputDir, { recursive: true });
41
+ const compiledDir = project.createDirectory(outputDir);
42
+ console.log(`Encontrados ${sourceFiles.length} archivos de workflow para procesar.`);
43
+ const workflowRegistry = [];
44
+ const generatedFiles = [];
45
+ for (const sourceFile of sourceFiles) {
46
+ console.log(`
47
+ Procesando archivo: ${sourceFile.getBaseName()}`);
48
+ const durableCalls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).filter((call) => call.getExpression().getText() === DURABLE_WRAPPER_NAME);
49
+ if (durableCalls.length === 0) continue;
50
+ for (const call of durableCalls) {
51
+ const workflowVarDecl = call.getParentIfKind(SyntaxKind.VariableDeclaration);
52
+ if (!workflowVarDecl) continue;
53
+ const workflowName = workflowVarDecl.getName();
54
+ console.log(` -> Transformando workflow: ${workflowName}`);
55
+ const [arg] = call.getArguments();
56
+ if (!Node.isObjectLiteralExpression(arg)) continue;
57
+ const workflowProperty = arg.getProperty("workflow");
58
+ if (!workflowProperty || !Node.isPropertyAssignment(workflowProperty)) continue;
59
+ const workflowFunc = workflowProperty.getInitializer();
60
+ if (!workflowFunc || !Node.isArrowFunction(workflowFunc)) continue;
61
+ const originalBaseName = sourceFile.getBaseName();
62
+ const compiledFileName = originalBaseName.replace(/\.ts$/, ".compiled.mts");
63
+ const newFilePath = path.join(compiledDir.getPath(), compiledFileName);
64
+ const newSourceFile = project.createSourceFile(newFilePath, "", {
65
+ overwrite: true
66
+ });
67
+ generatedFiles.push(newSourceFile);
68
+ transformWorkflowFunction(
69
+ workflowName,
70
+ workflowFunc,
71
+ call,
72
+ newSourceFile,
73
+ packageName
74
+ );
75
+ console.log(
76
+ ` -> Archivo generado: ${path.relative(process.cwd(), newFilePath)}`
77
+ );
78
+ const importPathName = compiledFileName;
79
+ workflowRegistry.push({ name: workflowName, importPath: `./${importPathName}` });
80
+ }
81
+ }
82
+ if (workflowRegistry.length > 0) {
83
+ const indexFilePath = path.join(compiledDir.getPath(), "index.mts");
84
+ const indexSourceFile = project.createSourceFile(indexFilePath, "", {
85
+ overwrite: true
86
+ });
87
+ generatedFiles.push(indexSourceFile);
88
+ indexSourceFile.addStatements(
89
+ "// Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.\n"
90
+ );
91
+ indexSourceFile.addImportDeclaration({
92
+ isTypeOnly: true,
93
+ moduleSpecifier: packageName,
94
+ namedImports: ["DurableFunction"]
95
+ });
96
+ for (const wf of workflowRegistry) {
97
+ indexSourceFile.addImportDeclaration({
98
+ moduleSpecifier: wf.importPath,
99
+ namedImports: [wf.name]
100
+ });
101
+ }
102
+ indexSourceFile.addExportDeclaration({
103
+ namedExports: workflowRegistry.map((wf) => wf.name)
104
+ });
105
+ indexSourceFile.addStatements("\n");
106
+ indexSourceFile.addVariableStatement({
107
+ declarationKind: VariableDeclarationKind.Const,
108
+ declarations: [
109
+ {
110
+ name: "durableFunctions",
111
+ type: "Map<string, DurableFunction<any, any, any>>",
112
+ initializer: "new Map()"
113
+ }
114
+ ]
115
+ });
116
+ const setStatements = workflowRegistry.map(
117
+ (wf) => `durableFunctions.set(${wf.name}.name, ${wf.name});`
118
+ );
119
+ indexSourceFile.addStatements(setStatements);
120
+ indexSourceFile.addStatements("\n");
121
+ indexSourceFile.addExportAssignment({
122
+ isExportEquals: false,
123
+ expression: "durableFunctions"
124
+ });
125
+ console.log(`
126
+ -> Archivo de \xEDndice generado: ${path.basename(indexFilePath)}`);
127
+ }
128
+ console.log("\nFormateando archivos generados con Prettier...");
129
+ for (const file of generatedFiles) {
130
+ await formatSourceFileWithPrettier(file);
131
+ }
132
+ await project.save();
133
+ console.log("\nCompilaci\xF3n completada exitosamente.");
134
+ }
135
+ function transformWorkflowFunction(workflowName, func, bDurableCall, targetSourceFile, packageName) {
136
+ const body = func.getBody();
137
+ if (!Node.isBlock(body)) {
138
+ throw new Error(`El cuerpo del workflow '${workflowName}' debe ser un bloque {}.`);
139
+ }
140
+ const { clauses: caseClauses } = processStatementsRecursive(body.getStatements(), {
141
+ step: 0,
142
+ persistedVariables: /* @__PURE__ */ new Map()
143
+ });
144
+ let returnTypeNode = func.getReturnType();
145
+ if (returnTypeNode.getSymbol()?.getName() === "Promise" && returnTypeNode.isObject()) {
146
+ returnTypeNode = returnTypeNode.getTypeArguments()[0] || returnTypeNode;
147
+ }
148
+ const returnType = returnTypeNode.getText(void 0, TYPE_FORMAT_FLAGS);
149
+ const dependencyStatements = /* @__PURE__ */ new Set();
150
+ const originalSourceFile = func.getSourceFile();
151
+ const typeArgs = bDurableCall.getTypeArguments();
152
+ const paramType = typeArgs.length > 0 ? typeArgs[0].getText() : "unknown";
153
+ const eventsType = typeArgs.length > 2 ? typeArgs[2].getText() : "Record<string, never>";
154
+ originalSourceFile.getImportDeclarations().forEach((importDecl) => {
155
+ if (importDecl.getModuleSpecifierValue() === packageName) return;
156
+ let moduleSpecifier = importDecl.getModuleSpecifierValue();
157
+ if (moduleSpecifier.includes(".workflow")) {
158
+ const parsedPath = path.parse(moduleSpecifier);
159
+ let joinedPath = path.join(parsedPath.dir, parsedPath.base + ".compiled.mts");
160
+ if (!joinedPath.startsWith(".") && !path.isAbsolute(joinedPath)) {
161
+ joinedPath = "./" + joinedPath;
162
+ }
163
+ moduleSpecifier = joinedPath.replace(/\\/g, "/");
164
+ } else if (moduleSpecifier.startsWith(".")) {
165
+ if (path.extname(moduleSpecifier) === "") {
166
+ moduleSpecifier += ".mjs";
167
+ }
168
+ }
169
+ const valueImports = [];
170
+ const typeImports = [];
171
+ importDecl.getNamedImports().forEach((specifier) => {
172
+ const name = specifier.getName();
173
+ const alias = specifier.getAliasNode()?.getText();
174
+ const importText = alias ? `${name} as ${alias}` : name;
175
+ const symbol = specifier.getNameNode().getSymbol()?.getAliasedSymbol() ?? specifier.getNameNode().getSymbol();
176
+ const declarations = symbol?.getDeclarations() ?? [];
177
+ const isEnum = declarations.some((d) => Node.isEnumDeclaration(d));
178
+ const isPurelyType = specifier.isTypeOnly() || !isEnum && declarations.every(
179
+ (d) => Node.isInterfaceDeclaration(d) || Node.isTypeAliasDeclaration(d)
180
+ );
181
+ if (isPurelyType) {
182
+ typeImports.push(importText);
183
+ } else {
184
+ valueImports.push(importText);
185
+ }
186
+ });
187
+ if (valueImports.length > 0) {
188
+ targetSourceFile.addImportDeclaration({
189
+ moduleSpecifier,
190
+ namedImports: valueImports
191
+ });
192
+ }
193
+ if (typeImports.length > 0) {
194
+ targetSourceFile.addImportDeclaration({
195
+ isTypeOnly: true,
196
+ moduleSpecifier,
197
+ namedImports: typeImports
198
+ });
199
+ }
200
+ const defaultImport = importDecl.getDefaultImport();
201
+ if (defaultImport) {
202
+ targetSourceFile.addImportDeclaration({
203
+ moduleSpecifier,
204
+ defaultImport: defaultImport.getText()
205
+ });
206
+ }
207
+ });
208
+ originalSourceFile.getInterfaces().forEach((iface) => {
209
+ dependencyStatements.add(
210
+ iface.getText().startsWith("export") ? iface.getText() : `export ${iface.getText()}`
211
+ );
212
+ });
213
+ originalSourceFile.getTypeAliases().forEach((typeAlias) => {
214
+ dependencyStatements.add(
215
+ typeAlias.getText().startsWith("export") ? typeAlias.getText() : `export ${typeAlias.getText()}`
216
+ );
217
+ });
218
+ const [params] = func.getParameters();
219
+ let paramAssignment = "";
220
+ if (params) {
221
+ const paramName = params.getNameNode().getText();
222
+ if (paramName !== "input") {
223
+ paramAssignment = `const ${paramName} = input;`;
224
+ }
225
+ }
226
+ targetSourceFile.addImportDeclaration({
227
+ isTypeOnly: true,
228
+ moduleSpecifier: packageName,
229
+ namedImports: ["DurableFunction", "WorkflowContext", "Instruction"]
230
+ });
231
+ if (dependencyStatements.size > 0) {
232
+ targetSourceFile.addStatements("\n");
233
+ targetSourceFile.addStatements(Array.from(dependencyStatements));
234
+ }
235
+ targetSourceFile.addStatements(
236
+ "\n// Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.\n"
237
+ );
238
+ const initializerObjectString = `{
12
239
  __isDurable: true,
13
- name: '${e}',
14
- async execute(context: WorkflowContext<${m}>): Promise<Instruction<${x}>> {
15
- const { input, state, result } = context;
16
- ${l}
17
- const ${E} = input;
18
- switch (context.step) {
19
- ${i.join(`
20
- `)}
21
- default:
22
- throw new Error(\`Paso desconocido: \${context.step}\`);
240
+ name: '${workflowName}',
241
+ async execute(context: WorkflowContext<${paramType}>): Promise<Instruction<${returnType}>> {
242
+ const { input, state, result, log, workflowId } = context;
243
+ ${paramAssignment}
244
+ while (true) {
245
+ switch (context.step) {
246
+ ${caseClauses.join("\n")}
247
+ default:
248
+ throw new Error(\`Paso desconocido: \${context.step}\`);
249
+ }
250
+ }
251
+ }
252
+ }`;
253
+ targetSourceFile.addVariableStatement({
254
+ isExported: true,
255
+ declarationKind: VariableDeclarationKind.Const,
256
+ declarations: [
257
+ {
258
+ name: workflowName,
259
+ type: `DurableFunction<${paramType}, ${returnType}, ${eventsType}>`,
260
+ initializer: initializerObjectString
261
+ }
262
+ ]
263
+ });
264
+ targetSourceFile.organizeImports();
265
+ }
266
+ function processStatementsRecursive(statements, initialState) {
267
+ if (statements.length === 0) {
268
+ const clauses = [];
269
+ if (initialState.pendingStateAssignment) {
270
+ const finalClause = `case ${initialState.step}: {
271
+ ${initialState.pendingStateAssignment}
272
+ return { type: 'COMPLETE', result: undefined };
273
+ }`;
274
+ clauses.push(finalClause);
275
+ }
276
+ return { clauses, nextStep: initialState.step + 1 };
277
+ }
278
+ const { syncBlock, durableStatement, nextStatements } = splitAtNextDurableCall(statements);
279
+ const { rewrittenSyncStatements, newlyPersistedVariables } = processSyncBlock(
280
+ syncBlock,
281
+ durableStatement ? [durableStatement, ...nextStatements] : [],
282
+ initialState.persistedVariables
283
+ );
284
+ if (initialState.pendingStateAssignment) {
285
+ rewrittenSyncStatements.unshift(initialState.pendingStateAssignment);
286
+ }
287
+ const updatedPersistedVariables = new Map([
288
+ ...initialState.persistedVariables,
289
+ ...newlyPersistedVariables
290
+ ]);
291
+ if (!durableStatement) {
292
+ const syncCode2 = rewrittenSyncStatements.join("\n");
293
+ const lastStatementIsReturn = syncBlock.length > 0 && Node.isReturnStatement(syncBlock[syncBlock.length - 1]);
294
+ const finalInstruction = lastStatementIsReturn ? "" : `
295
+ return { type: 'COMPLETE', result: undefined };`;
296
+ const clause = `case ${initialState.step}: {
297
+ ${syncCode2}${finalInstruction}
298
+ }`;
299
+ return { clauses: [clause], nextStep: initialState.step + 1 };
300
+ }
301
+ if (Node.isIfStatement(durableStatement)) {
302
+ return processIfStatement(
303
+ durableStatement,
304
+ nextStatements,
305
+ {
306
+ ...initialState,
307
+ persistedVariables: updatedPersistedVariables
308
+ },
309
+ rewrittenSyncStatements
310
+ );
311
+ }
312
+ if (Node.isTryStatement(durableStatement)) {
313
+ return processTryStatement(
314
+ durableStatement,
315
+ nextStatements,
316
+ { ...initialState, persistedVariables: updatedPersistedVariables },
317
+ rewrittenSyncStatements
318
+ );
319
+ }
320
+ const { instruction, nextPendingStateAssignment } = generateDurableInstruction(
321
+ durableStatement,
322
+ updatedPersistedVariables
323
+ );
324
+ rewrittenSyncStatements.push(instruction);
325
+ const syncCode = rewrittenSyncStatements.join("\n");
326
+ const currentClause = `case ${initialState.step}: {
327
+ ${syncCode}
328
+ }`;
329
+ const nextState = {
330
+ step: initialState.step + 1,
331
+ persistedVariables: updatedPersistedVariables,
332
+ pendingStateAssignment: nextPendingStateAssignment
333
+ };
334
+ const restResult = processStatementsRecursive(nextStatements, nextState);
335
+ return {
336
+ clauses: [currentClause, ...restResult.clauses],
337
+ nextStep: restResult.nextStep
338
+ };
339
+ }
340
+ function processIfStatement(ifStatement, followingStatements, currentState, rewrittenSyncStatements) {
341
+ const condition = rewriteNode(ifStatement.getExpression(), currentState.persistedVariables);
342
+ const thenBlock = ifStatement.getThenStatement();
343
+ const thenStatements = Node.isBlock(thenBlock) ? thenBlock.getStatements() : [thenBlock];
344
+ const thenResult = processStatementsRecursive(thenStatements, {
345
+ step: currentState.step + 1,
346
+ persistedVariables: new Map(currentState.persistedVariables)
347
+ // Clona para aislamiento de ramas
348
+ });
349
+ let elseResult;
350
+ const elseStatement = ifStatement.getElseStatement();
351
+ if (elseStatement) {
352
+ const elseStatements = Node.isBlock(elseStatement) ? elseStatement.getStatements() : [elseStatement];
353
+ elseResult = processStatementsRecursive(elseStatements, {
354
+ step: thenResult.nextStep,
355
+ // La rama 'else' comienza donde termina la 'then'
356
+ persistedVariables: new Map(currentState.persistedVariables)
357
+ });
358
+ }
359
+ const nextStepAfterIf = elseResult ? elseResult.nextStep : thenResult.nextStep;
360
+ const afterIfResult = processStatementsRecursive(followingStatements, {
361
+ step: nextStepAfterIf,
362
+ persistedVariables: currentState.persistedVariables
363
+ // Usa el mapa original
364
+ });
365
+ const syncCode = rewrittenSyncStatements.join("\n");
366
+ const jumpToElseStep = thenResult.nextStep;
367
+ const ifClause = `
368
+ case ${currentState.step}: {
369
+ ${syncCode}
370
+ if (${condition}) {
371
+ context.step = ${currentState.step + 1};
372
+ } else {
373
+ ${elseStatement ? `context.step = ${jumpToElseStep};` : `context.step = ${nextStepAfterIf};`}
23
374
  }
375
+ break;
376
+ }
377
+ `;
378
+ return {
379
+ clauses: [
380
+ ifClause,
381
+ ...thenResult.clauses,
382
+ ...elseResult ? elseResult.clauses : [],
383
+ ...afterIfResult.clauses
384
+ ],
385
+ nextStep: afterIfResult.nextStep
386
+ };
387
+ }
388
+ function processTryStatement(tryStatement, followingStatements, currentState, rewrittenSyncStatements) {
389
+ const { step, persistedVariables } = currentState;
390
+ const tryBlock = tryStatement.getTryBlock();
391
+ const catchClause = tryStatement.getCatchClause();
392
+ const finallyBlock = tryStatement.getFinallyBlock();
393
+ const tryResult = processStatementsRecursive(tryBlock.getStatements(), {
394
+ step: step + 1,
395
+ // El bloque try comienza en el siguiente step
396
+ persistedVariables: new Map(persistedVariables)
397
+ });
398
+ let catchResult;
399
+ let catchVarName;
400
+ const catchStep = tryResult.nextStep;
401
+ if (catchClause) {
402
+ const catchBlock = catchClause.getBlock();
403
+ const varDeclaration = catchClause.getVariableDeclaration();
404
+ if (varDeclaration) {
405
+ catchVarName = varDeclaration.getName();
406
+ }
407
+ catchResult = processStatementsRecursive(catchBlock.getStatements(), {
408
+ step: catchStep,
409
+ persistedVariables: new Map(persistedVariables)
410
+ });
411
+ }
412
+ let finallyResult;
413
+ const finallyStep = catchResult ? catchResult.nextStep : catchStep;
414
+ if (finallyBlock) {
415
+ finallyResult = processStatementsRecursive(finallyBlock.getStatements(), {
416
+ step: finallyStep,
417
+ persistedVariables: new Map(persistedVariables)
418
+ });
419
+ }
420
+ const endStep = finallyResult ? finallyResult.nextStep : finallyStep;
421
+ const afterTryResult = processStatementsRecursive(followingStatements, {
422
+ step: endStep,
423
+ persistedVariables
424
+ });
425
+ const handler = `{ catchStep: ${catchClause ? catchStep : "undefined"}, finallyStep: ${finallyBlock ? finallyStep : "undefined"} }`;
426
+ const pushClause = `
427
+ case ${step}: {
428
+ ${rewrittenSyncStatements.join("\n")}
429
+ state.tryCatchStack = state.tryCatchStack || [];
430
+ state.tryCatchStack.push(${handler});
431
+ context.step = ${step + 1}; // Salta al inicio del bloque try
432
+ break;
433
+ }
434
+ `;
435
+ const lastTryClause = tryResult.clauses.pop() || "";
436
+ const jumpToAfterTryStep = finallyBlock ? finallyStep : endStep;
437
+ tryResult.clauses.push(
438
+ lastTryClause.replace(
439
+ /return { type: 'COMPLETE'.* };/,
440
+ `context.step = ${jumpToAfterTryStep}; break;`
441
+ )
442
+ );
443
+ if (catchResult) {
444
+ if (catchVarName) {
445
+ const firstCatchClause = catchResult.clauses[0] || `case ${catchStep}: {}`;
446
+ catchResult.clauses[0] = firstCatchClause.replace(
447
+ "{",
448
+ `{
449
+ const ${catchVarName} = result as Error;`
450
+ );
451
+ }
452
+ const lastCatchClause = catchResult.clauses.pop() || "";
453
+ catchResult.clauses.push(
454
+ lastCatchClause.replace(
455
+ /return { type: 'COMPLETE'.* };/,
456
+ `context.step = ${jumpToAfterTryStep}; break;`
457
+ )
458
+ );
459
+ }
460
+ if (finallyResult) {
461
+ const lastFinallyClause = finallyResult.clauses.pop() || "";
462
+ finallyResult.clauses.push(
463
+ lastFinallyClause.replace(
464
+ /return { type: 'COMPLETE'.* };/,
465
+ `state.tryCatchStack?.pop(); context.step = ${endStep}; break;`
466
+ )
467
+ );
468
+ }
469
+ return {
470
+ clauses: [
471
+ pushClause,
472
+ ...tryResult.clauses,
473
+ ...catchResult ? catchResult.clauses : [],
474
+ ...finallyResult ? finallyResult.clauses : [],
475
+ ...afterTryResult.clauses
476
+ ],
477
+ nextStep: afterTryResult.nextStep
478
+ };
479
+ }
480
+ function processSyncBlock(syncBlock, followingStatements, persistedVariables) {
481
+ const rewrittenSyncStatements = [];
482
+ const newlyPersistedVariables = /* @__PURE__ */ new Map();
483
+ const usedLaterIdentifiers = findUsedIdentifiers(followingStatements);
484
+ const currentPersistedVars = new Map(persistedVariables);
485
+ for (const statement of syncBlock) {
486
+ let handledAsStatePersistence = false;
487
+ if (Node.isVariableStatement(statement)) {
488
+ for (const decl of statement.getDeclarations()) {
489
+ const initializer = decl.getInitializer();
490
+ if (!initializer) continue;
491
+ const varInfos = getVarInfoFromDeclaration(decl);
492
+ const varsToPersist = varInfos.filter(
493
+ (v) => usedLaterIdentifiers.has(v.name)
494
+ );
495
+ if (varsToPersist.length > 0) {
496
+ const rewrittenInitializer = rewriteNode(
497
+ initializer,
498
+ persistedVariables
499
+ );
500
+ for (const { name, type } of varsToPersist) {
501
+ newlyPersistedVariables.set(name, { type });
502
+ currentPersistedVars.set(name, { type });
503
+ const assignmentRightHand = varInfos.length > 1 ? `${rewrittenInitializer}.${name}` : rewrittenInitializer;
504
+ rewrittenSyncStatements.push(
505
+ `state.${name} = ${assignmentRightHand};`
506
+ );
507
+ }
508
+ if (varsToPersist.length === varInfos.length) {
509
+ handledAsStatePersistence = true;
510
+ }
511
+ }
512
+ }
513
+ }
514
+ if (!handledAsStatePersistence) {
515
+ rewrittenSyncStatements.push(rewriteNode(statement, currentPersistedVars));
516
+ }
517
+ }
518
+ return { rewrittenSyncStatements, newlyPersistedVariables };
519
+ }
520
+ function getVarInfoFromDeclaration(decl) {
521
+ const nameNode = decl.getNameNode();
522
+ const result = [];
523
+ if (Node.isIdentifier(nameNode)) {
524
+ const type = decl.getType().getText(decl, TYPE_FORMAT_FLAGS);
525
+ result.push({ name: nameNode.getText(), type });
526
+ } else if (Node.isObjectBindingPattern(nameNode)) {
527
+ for (const element of nameNode.getElements()) {
528
+ const name = element.getName();
529
+ const type = element.getType().getText(element, TYPE_FORMAT_FLAGS);
530
+ result.push({ name, type });
531
+ }
532
+ }
533
+ return result;
534
+ }
535
+ function generateDurableInstruction(statement, persistedVariables) {
536
+ if (Node.isReturnStatement(statement)) {
537
+ const returnExpr = statement.getExpression() ? rewriteNode(statement.getExpressionOrThrow(), persistedVariables) : "undefined";
538
+ return {
539
+ instruction: `return { type: 'COMPLETE', result: ${returnExpr} };`,
540
+ nextPendingStateAssignment: void 0
541
+ };
542
+ }
543
+ let nextPendingStateAssignment = void 0;
544
+ const varDecl = statement.getFirstDescendantByKind(SyntaxKind.VariableDeclaration);
545
+ if (varDecl) {
546
+ const varName = varDecl.getName();
547
+ const varType = varDecl.getType().getText(varDecl, TYPE_FORMAT_FLAGS);
548
+ persistedVariables.set(varName, { type: varType });
549
+ nextPendingStateAssignment = `state.${varName} = result;`;
550
+ }
551
+ const awaitExpression = statement.getFirstDescendantByKind(SyntaxKind.AwaitExpression);
552
+ if (awaitExpression) {
553
+ const callExpr = awaitExpression.getExpression();
554
+ if (Node.isCallExpression(callExpr)) {
555
+ return {
556
+ instruction: `return ${createInstructionForCall(callExpr, persistedVariables)};`,
557
+ nextPendingStateAssignment
558
+ };
559
+ }
560
+ }
561
+ return {
562
+ instruction: rewriteNode(statement, persistedVariables),
563
+ nextPendingStateAssignment
564
+ };
565
+ }
566
+ function findUsedIdentifiers(nodes) {
567
+ const identifiers = /* @__PURE__ */ new Set();
568
+ for (const node of nodes) {
569
+ node.getDescendantsOfKind(SyntaxKind.Identifier).forEach((ident) => {
570
+ identifiers.add(ident.getText());
571
+ });
572
+ }
573
+ return identifiers;
574
+ }
575
+ function rewriteNode(node, persistedVariables) {
576
+ const tempSourceFile = node.getProject().createSourceFile(
577
+ `temp_rewrite_${Math.random()}.ts`,
578
+ `const temp = ${node.getText()};`,
579
+ // Se envuelve para asegurar que sea un AST válido
580
+ { overwrite: true }
581
+ );
582
+ const nodeClone = tempSourceFile.getVariableDeclarationOrThrow("temp").getInitializerOrThrow();
583
+ const descendants = [nodeClone, ...nodeClone.getDescendants()].reverse();
584
+ for (const currentNode of descendants) {
585
+ if (Node.isIdentifier(currentNode) && !currentNode.wasForgotten() && persistedVariables.has(currentNode.getText())) {
586
+ const varName = currentNode.getText();
587
+ const parent = currentNode.getParent();
588
+ const isDeclaration = Node.isVariableDeclaration(parent) && parent.getNameNode() === currentNode;
589
+ const isProperty = Node.isPropertyAccessExpression(parent) && parent.getNameNode() === currentNode || Node.isPropertyAssignment(parent) && parent.getNameNode() === currentNode;
590
+ const isBindingElement = Node.isBindingElement(parent) && parent.getNameNode() === currentNode;
591
+ if (!isDeclaration && !isProperty && !isBindingElement) {
592
+ const typeInfo = persistedVariables.get(varName);
593
+ currentNode.replaceWithText(`(state.${varName} as ${typeInfo.type})`);
594
+ }
595
+ }
596
+ }
597
+ const fullText = tempSourceFile.getFullText().trim();
598
+ tempSourceFile.forget();
599
+ const prefix = "const temp = ";
600
+ let newText = fullText;
601
+ if (newText.startsWith(prefix)) {
602
+ newText = newText.substring(prefix.length);
603
+ }
604
+ if (newText.endsWith(";")) {
605
+ newText = newText.slice(0, -1);
606
+ }
607
+ return newText;
608
+ }
609
+ function createInstructionForCall(callExpr, persistedVariables) {
610
+ const identifier = callExpr.getExpression();
611
+ let functionName;
612
+ let isContextCall = false;
613
+ if (Node.isPropertyAccessExpression(identifier)) {
614
+ const expr = identifier.getExpression();
615
+ if (expr.getText() === "context") {
616
+ isContextCall = true;
617
+ }
618
+ functionName = identifier.getName();
619
+ } else {
620
+ functionName = identifier.getText();
621
+ }
622
+ const args = callExpr.getArguments().map((arg) => rewriteNode(arg, persistedVariables)).join(", ");
623
+ if (isContextCall) {
624
+ if (functionName === "bSleep") {
625
+ return `{ type: 'SCHEDULE_SLEEP', duration: ${args} }`;
626
+ }
627
+ if (functionName === "bWaitForEvent") {
628
+ return `{ type: 'WAIT_FOR_EVENT', eventName: ${args} }`;
629
+ }
630
+ if (functionName === "bExecute") {
631
+ const [workflowArg, inputArg] = callExpr.getArguments();
632
+ const subWorkflowName = workflowArg.getText();
633
+ const subWorkflowInput = inputArg ? rewriteNode(inputArg, persistedVariables) : "undefined";
634
+ return `{ type: 'EXECUTE_SUBWORKFLOW', workflowName: ${subWorkflowName}.name, input: ${subWorkflowInput} }`;
635
+ }
636
+ throw new Error(`Funci\xF3n de contexto durable desconocida: '${functionName}'.`);
637
+ }
638
+ const symbol = identifier.getSymbol();
639
+ if (!symbol) throw new Error(`S\xEDmbolo no encontrado para '${functionName}'.`);
640
+ const importSpecifier = symbol.getDeclarations()[0]?.asKind(SyntaxKind.ImportSpecifier);
641
+ if (!importSpecifier) throw new Error(`'${functionName}' debe ser importada.`);
642
+ const sourceFile = importSpecifier.getImportDeclaration().getModuleSpecifierSourceFileOrThrow();
643
+ const relativeSourcePath = path.relative(process.cwd(), sourceFile.getFilePath()).replace(/\\/g, "/");
644
+ return `{ type: 'SCHEDULE_TASK', modulePath: '${relativeSourcePath}', exportName: '${functionName}', args: [${args}] }`;
645
+ }
646
+ function hasDurableCall(node) {
647
+ for (const awaitExpr of node.getDescendantsOfKind(SyntaxKind.AwaitExpression)) {
648
+ const callExpr = awaitExpr.getExpressionIfKind(SyntaxKind.CallExpression);
649
+ if (callExpr) {
650
+ const expression = callExpr.getExpression();
651
+ if (Node.isPropertyAccessExpression(expression)) {
652
+ const propName = expression.getName();
653
+ if (expression.getExpression().getText() === "context" && (propName === "bSleep" || propName === "bWaitForEvent" || propName === "bExecute") || expression.getSymbol()?.getDeclarations()[0]?.isKind(SyntaxKind.ImportSpecifier)) {
654
+ return true;
655
+ }
656
+ } else {
657
+ if (expression.getSymbol()?.getDeclarations()[0]?.isKind(SyntaxKind.ImportSpecifier)) {
658
+ return true;
24
659
  }
25
- }`;r.addVariableStatement({isExported:!0,declarationKind:W.Const,declarations:[{name:e,type:`DurableFunction<${m}, ${x}>`,initializer:T}]})}function F(e,t,n,o,r,a){if(e.length===0)return{clauses:[],nextStep:t};let{syncBlock:u,durableStatement:i,nextStatements:c}=A(e),s=a+u.map(m=>h(m,n,o)).join(`
26
- `);if(!i)return{clauses:[`case ${t}: {
27
- ${s}
28
- return { type: 'COMPLETE', result: undefined };
29
- }`],nextStep:t+1};let d="",l=i.getFirstDescendantByKind($.VariableDeclaration);if(l){let m=l.getName(),f=l.getTypeNode()?.getText()??l.getInitializerOrThrow().getType().getText();n.set(m,{type:f}),d=`state.${m} = result;`}if(g.isIfStatement(i)){let m=h(i.getExpression(),n,o),f=`__branch_step_${t}`,T=L(i.getThenStatement()),S=i.getElseStatement(),I=S?L(S):[],{syncBlock:z,durableStatement:B,nextStatements:H}=A(T),{syncBlock:q,durableStatement:k,nextStatements:X}=A(I),G=z.map(D=>h(D,n,o)).join(`
30
- `),J=B?v(B,n,o,r):'return { type: "COMPLETE", result: undefined };',M=`if (${m}) {
31
- ${G}
32
- state.${f} = 0;
33
- ${J}
34
- }`;if(I.length>0){let D=q.map(N=>h(N,n,o)).join(`
35
- `),C=k?v(k,n,o,r):'return { type: "COMPLETE", result: undefined };';M+=` else {
36
- ${D}
37
- state.${f} = 1;
38
- ${C}
39
- }`}let Q=`case ${t}: {
40
- ${s}
41
- ${M}
42
- }`,O=F(H,t+1,new Map(n),o,r,d),_=F(X,t+1,new Map(n),o,r,d),P=[];if(O.clauses.length>0||_.clauses.length>0){let D=O.clauses.map(w=>w.replace(/^case \d+:\s*{/,"")).map(w=>w.replace(/}\s*$/,"")).join(`
43
- `),C=_.clauses.map(w=>w.replace(/^case \d+:\s*{/,"")).map(w=>w.replace(/}\s*$/,"")).join(`
44
- `),N=`case ${t+1}: {
45
- if (state.${f} === 0) {
46
- ${D}
47
- } else {
48
- ${C}
49
- }
50
- }`;P.push(N)}let Y=t+1+(P.length>0?1:0),R=F(c,Y,n,o,r,"");return{clauses:[Q,...P,...R.clauses],nextStep:R.nextStep}}let x=v(i,n,o,r),p=`case ${t}: {
51
- ${s}
52
- ${x}
53
- }`,E=F(c,t+1,n,o,r,d);return{clauses:[p,...E.clauses],nextStep:E.nextStep}}function b(e){if(e.getDescendantsOfKind($.AwaitExpression).length>0)return!0;if(g.isIfStatement(e)){let t=b(e.getThenStatement()),n=e.getElseStatement()?b(e.getElseStatement()):!1;return t||n}return g.isBlock(e)?e.getStatements().some(b):!1}function L(e){return g.isBlock(e)?e.getStatements():[e]}function A(e){for(let t=0;t<e.length;t++){let n=e[t];if(g.isReturnStatement(n)||b(n))return{syncBlock:e.slice(0,t),durableStatement:n,nextStatements:e.slice(t+1)}}return{syncBlock:e,durableStatement:null,nextStatements:[]}}function v(e,t,n,o){if(g.isReturnStatement(e))return`return { type: 'COMPLETE', result: ${e.getExpression()?h(e.getExpressionOrThrow(),t,n):"undefined"} };`;let r=e.getFirstDescendantByKind($.AwaitExpression);if(r){let a=r.getExpression();if(g.isCallExpression(a))return`return ${re(a,t,n,o)};`}throw new Error(`No se pudo generar una instrucci\xF3n para el statement: ${e.getText()}`)}function re(e,t,n,o){let r=e.getExpression(),a=r.getText(),u=e.getArguments().map(d=>h(d,t,n)).join(", "),i=r.getSymbol();if(!i)throw new Error(`S\xEDmbolo no encontrado para '${a}'.`);let c=i.getDeclarations()[0]?.asKind($.ImportSpecifier);if(!c)throw new Error(`'${a}' debe ser importada.`);if(c.getImportDeclaration().getModuleSpecifierValue()===o){if(a==="bSleep")return`{ type: 'SCHEDULE_SLEEP', duration: ${u} }`;if(a==="bWaitForEvent")return`{ type: 'WAIT_FOR_EVENT', eventName: ${u} }`;if(a==="bExecute"){let[d,l]=e.getArguments(),x=d.getText(),p=h(l,t,n);return`{ type: 'EXECUTE_SUBWORKFLOW', workflowName: '${x}', input: ${p} }`}throw new Error(`Funci\xF3n desconocida '${a}' importada desde '${o}'.`)}else{let d=c.getImportDeclaration().getModuleSpecifierSourceFileOrThrow();return`{ type: 'SCHEDULE_TASK', modulePath: '${y.relative(process.cwd(),d.getFilePath()).replace(/\\/g,"/")}', exportName: '${a}', args: [${u}] }`}}function h(e,t,n){let o=e.getProject().createSourceFile("temp_rewrite.ts",e.getText(),{overwrite:!0}).getChildAtIndex(0),r=[o,...o.getDescendants()];for(let u of r.reverse())if(g.isIdentifier(u)&&!u.wasForgotten()){let i=u.getText();if(t.has(i)){let c=t.get(i);if(c){let s=u.getParent();if(s&&g.isPropertyAccessExpression(s)&&s.getNameNode()===u||s&&g.isPropertyAssignment(s)&&s.getNameNode()===u)continue;u.replaceWithText(`(state.${i} as ${c.type})`)}}}let a=o.getSourceFile().getFullText();return o.getSourceFile().forget(),a}var U=e=>{let t=process.argv.indexOf(e);if(t!==-1&&process.argv.length>t+1)return process.argv[t+1]};async function se(){let e=U("--in"),t=U("--out");(!e||!t)&&(console.error("Uso: b-durable-compiler --in <directorio_entrada> --out <directorio_salida>"),process.exit(1));let n=j.resolve(process.cwd(),e),o=j.resolve(process.cwd(),t);await K({inputDir:n,outputDir:o,packageName:"@bobtail.software/b-durable"})}se().catch(e=>{console.error("Error durante la compilaci\xF3n:",e),process.exit(1)});
660
+ }
661
+ }
662
+ }
663
+ if (Node.isTryStatement(node)) {
664
+ if (hasDurableCall(node.getTryBlock())) return true;
665
+ if (node.getCatchClause() && hasDurableCall(node.getCatchClause().getBlock()))
666
+ return true;
667
+ if (node.getFinallyBlock() && hasDurableCall(node.getFinallyBlock())) return true;
668
+ }
669
+ if (Node.isIfStatement(node)) {
670
+ const thenHas = hasDurableCall(node.getThenStatement());
671
+ const elseHas = node.getElseStatement() ? hasDurableCall(node.getElseStatement()) : false;
672
+ return thenHas || elseHas;
673
+ }
674
+ if (Node.isBlock(node)) {
675
+ return node.getStatements().some(hasDurableCall);
676
+ }
677
+ return false;
678
+ }
679
+ function splitAtNextDurableCall(statements) {
680
+ for (let i = 0; i < statements.length; i++) {
681
+ const statement = statements[i];
682
+ if (Node.isReturnStatement(statement) || hasDurableCall(statement) || Node.isTryStatement(statement)) {
683
+ return {
684
+ syncBlock: statements.slice(0, i),
685
+ durableStatement: statement,
686
+ nextStatements: statements.slice(i + 1)
687
+ };
688
+ }
689
+ }
690
+ return { syncBlock: statements, durableStatement: null, nextStatements: [] };
691
+ }
692
+
693
+ // src/compiler/cli.ts
694
+ var getArg = (name) => {
695
+ const argIndex = process.argv.indexOf(name);
696
+ if (argIndex !== -1 && process.argv.length > argIndex + 1) {
697
+ return process.argv[argIndex + 1];
698
+ }
699
+ return void 0;
700
+ };
701
+ async function run() {
702
+ const inputDir = getArg("--in");
703
+ const outputDir = getArg("--out");
704
+ if (!inputDir || !outputDir) {
705
+ console.error(
706
+ "Uso: b-durable-compiler --in <directorio_entrada> --out <directorio_salida>"
707
+ );
708
+ process.exit(1);
709
+ }
710
+ const absInput = path2.resolve(process.cwd(), inputDir);
711
+ const absOutput = path2.resolve(process.cwd(), outputDir);
712
+ await compileWorkflows({
713
+ inputDir: absInput,
714
+ outputDir: absOutput,
715
+ packageName: "@bobtail.software/b-durable"
716
+ });
717
+ }
718
+ run().catch((error) => {
719
+ console.error("Error durante la compilaci\xF3n:", error);
720
+ process.exit(1);
721
+ });