@bobtail.software/b-durable 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/cli.mjs +48 -38
- package/package.json +1 -1
package/dist/compiler/cli.mjs
CHANGED
|
@@ -1,64 +1,74 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
Procesando archivo: ${
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import q from"path";import{createHash as X}from"crypto";import{existsSync as K,mkdirSync as Y,readFileSync as Q,rmSync as Z,writeFileSync as ee}from"fs";import N from"path";import*as j from"prettier";import{Node as a,Project as te,SyntaxKind as P,ts as U,VariableDeclarationKind as _}from"ts-morph";var ne="bDurable",L=U.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope|U.TypeFormatFlags.NoTruncation;function se(e){return X("sha256").update(e).digest("hex")}async function re(e){let t=e.getFilePath(),o=e.getFullText(),n=await j.resolveConfig(t),i=await j.format(o,{...n,parser:"typescript"});e.replaceWithText(i)}async function H(e){console.log("Iniciando compilador de workflows duraderos...");let{inputDir:t,outputDir:o,packageName:n,mode:i}=e,c=N.resolve(process.cwd(),"durable.lock.json"),l={};if(K(c))try{l=JSON.parse(Q(c,"utf-8"))}catch(p){console.warn("Advertencia: durable.lock.json corrupto, iniciando uno nuevo.",p)}let r=new te({tsConfigFilePath:N.resolve(process.cwd(),"tsconfig.json")}),g=r.addSourceFilesAtPaths(`${t}/**/*.ts`);K(o)&&(console.log(`Limpiando directorio de salida: ${o}`),Z(o,{recursive:!0,force:!0})),Y(o,{recursive:!0});let d=r.createDirectory(o);console.log(`Encontrados ${g.length} archivos de workflow para procesar.`);let s=[],S=[],m=!1;for(let p of g){console.log(`
|
|
3
|
+
Procesando archivo: ${p.getBaseName()}`);let u=p.getDescendantsOfKind(P.CallExpression).filter(y=>y.getExpression().getText()===ne);if(u.length!==0)for(let y of u){let f=y.getParentIfKind(P.VariableDeclaration);if(!f)continue;let b=f.getName();console.log(` -> Transformando workflow: ${b}`);let[v]=y.getArguments();if(!a.isObjectLiteralExpression(v))continue;let I=v.getProperty("workflow");if(!I||!a.isPropertyAssignment(I))continue;let $=I.getInitializer();if(!$||!a.isArrowFunction($))continue;let w=v.getProperty("version"),h="unknown";if(w&&a.isPropertyAssignment(w)){let R=w.getInitializer();R&&a.isStringLiteral(R)&&(h=R.getLiteralValue())}let x=p.getBaseName().replace(/\.ts$/,".compiled.mts"),E=N.join(d.getPath(),x),C=r.createSourceFile(E,"",{overwrite:!0});S.push(C),oe(b,$,y,C,n);let M=C.getFullText(),F=se(M),T=l[b]?.[h];if(T)if(T!==F){if(i==="prod")throw new Error(`
|
|
4
|
+
\u{1F6D1} ERROR DE INTEGRIDAD (PROD) \u{1F6D1}
|
|
5
|
+
El workflow '${b}' (v${h}) ha cambiado, pero el lockfile no se actualiz\xF3.
|
|
6
|
+
Hash esperado: ${T.substring(0,8)}...
|
|
7
|
+
Hash actual: ${F.substring(0,8)}...
|
|
8
|
+
|
|
9
|
+
Soluci\xF3n:
|
|
10
|
+
1. Incrementa la versi\xF3n en tu c\xF3digo (ej: v${h} -> v${parseFloat(h)+.1}).
|
|
11
|
+
2. O ejecuta en modo dev para actualizar el lockfile: 'b-durable-compiler dev ...'
|
|
12
|
+
`);console.log(` \u26A0\uFE0F v${h} modificada. Actualizando hash en lockfile.`),l[b]||(l[b]={}),l[b][h]=F,m=!0}else console.log(` \u2705 v${h} verificada.`);else console.log(` \u{1F195} v${h} registrada.`),l[b]||(l[b]={}),l[b][h]=F,m=!0;console.log(` -> Archivo generado: ${N.relative(process.cwd(),E)}`);let D=x;s.push({name:b,importPath:`./${D}`})}}if(m){if(i==="prod")throw new Error("El lockfile ha cambiado durante un build de PROD. Esto no est\xE1 permitido.");ee(c,JSON.stringify(l,null,2)),console.log(`
|
|
13
|
+
\u{1F512} durable.lock.json actualizado.`)}if(s.length>0){let p=N.join(d.getPath(),"index.mts"),u=r.createSourceFile(p,"",{overwrite:!0});S.push(u),u.addStatements(`// Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.
|
|
14
|
+
`),u.addImportDeclaration({isTypeOnly:!0,moduleSpecifier:n,namedImports:["DurableFunction"]});for(let f of s)u.addImportDeclaration({moduleSpecifier:f.importPath,namedImports:[f.name]});u.addExportDeclaration({namedExports:s.map(f=>f.name)}),u.addStatements(`
|
|
15
|
+
`),u.addVariableStatement({declarationKind:_.Const,declarations:[{name:"durableFunctions",type:"Map<string, DurableFunction<any, any, any, any>>",initializer:"new Map()"}]});let y=s.map(f=>`durableFunctions.set(${f.name}.name, ${f.name});`);u.addStatements(y),u.addStatements(`
|
|
16
|
+
`),u.addExportAssignment({isExportEquals:!1,expression:"durableFunctions"}),console.log(`
|
|
17
|
+
-> Archivo de \xEDndice generado: ${N.basename(p)}`)}console.log(`
|
|
18
|
+
Formateando archivos generados con Prettier...`);for(let p of S)await re(p);await r.save(),console.log(`
|
|
19
|
+
Compilaci\xF3n completada exitosamente.`)}function oe(e,t,o,n,i){let c=t.getBody();if(!a.isBlock(c))throw new Error(`El cuerpo del workflow '${e}' debe ser un bloque {}.`);let[l]=o.getArguments();if(!a.isObjectLiteralExpression(l))throw new Error("El argumento de bDurable debe ser un objeto.");let r=l.getProperty("version");if(!r||!a.isPropertyAssignment(r))throw new Error(`El workflow '${e}' debe tener una propiedad 'version'.`);let g=r.getInitializer();if(!g||!a.isStringLiteral(g))throw new Error(`La versi\xF3n del workflow '${e}' debe ser un string literal.`);let d=l.getProperty("retryOptions"),s="undefined";d&&a.isPropertyAssignment(d)&&(s=d.getInitializer()?.getText()||"undefined");let{clauses:S}=A(c.getStatements(),{step:0,persistedVariables:new Map}),m=t.getReturnType();m.getSymbol()?.getName()==="Promise"&&m.isObject()&&(m=m.getTypeArguments()[0]||m);let p=m.getText(void 0,L),u=new Set,y=t.getSourceFile(),f=o.getTypeArguments(),b=f.length>0?f[0].getText():"unknown",v=f.length>2?f[2].getText():"Record<string, never>",I=f.length>3?f[3].getText():"Record<string, never>";y.getImportDeclarations().forEach(x=>{if(x.getModuleSpecifierValue()===i)return;let E=x.getModuleSpecifierValue();if(E.includes(".workflow")){let T=N.parse(E),D=N.join(T.dir,T.base+".compiled.mts");!D.startsWith(".")&&!N.isAbsolute(D)&&(D="./"+D),E=D.replace(/\\/g,"/")}else E.startsWith(".")&&N.extname(E)===""&&(E+=".mjs");let C=[],M=[];x.getNamedImports().forEach(T=>{let D=T.getName(),R=T.getAliasNode()?.getText(),W=R?`${D} as ${R}`:D,z=(T.getNameNode().getSymbol()?.getAliasedSymbol()??T.getNameNode().getSymbol())?.getDeclarations()??[],J=z.some(B=>a.isEnumDeclaration(B));T.isTypeOnly()||!J&&z.every(B=>a.isInterfaceDeclaration(B)||a.isTypeAliasDeclaration(B))?M.push(W):C.push(W)}),C.length>0&&n.addImportDeclaration({moduleSpecifier:E,namedImports:C}),M.length>0&&n.addImportDeclaration({isTypeOnly:!0,moduleSpecifier:E,namedImports:M});let F=x.getDefaultImport();F&&n.addImportDeclaration({moduleSpecifier:E,defaultImport:F.getText()})}),y.getInterfaces().forEach(x=>{u.add(x.getText().startsWith("export")?x.getText():`export ${x.getText()}`)}),y.getTypeAliases().forEach(x=>{u.add(x.getText().startsWith("export")?x.getText():`export ${x.getText()}`)});let[$]=t.getParameters(),w="";if($){let x=$.getNameNode().getText();x!=="input"&&(w=`const ${x} = input;`)}n.addImportDeclaration({isTypeOnly:!0,moduleSpecifier:i,namedImports:["DurableFunction","WorkflowContext","Instruction"]}),u.size>0&&(n.addStatements(`
|
|
20
|
+
`),n.addStatements(Array.from(u))),n.addStatements(`
|
|
11
21
|
// Este archivo fue generado autom\xE1ticamente. NO EDITAR MANUALMENTE.
|
|
12
|
-
`);let
|
|
22
|
+
`);let h=g.getLiteralValue(),V=`{
|
|
13
23
|
__isDurable: true,
|
|
14
24
|
name: '${e}',
|
|
15
|
-
version: '${
|
|
16
|
-
retryOptions: ${
|
|
17
|
-
async execute(context: WorkflowContext<${
|
|
25
|
+
version: '${h}',
|
|
26
|
+
retryOptions: ${s},
|
|
27
|
+
async execute(context: WorkflowContext<${b}>): Promise<Instruction<${p}>> {
|
|
18
28
|
const { input, state, result, log, workflowId } = context;
|
|
19
|
-
${
|
|
29
|
+
${w}
|
|
20
30
|
while (true) {
|
|
21
31
|
switch (context.step) {
|
|
22
|
-
${
|
|
32
|
+
${S.join(`
|
|
23
33
|
`)}
|
|
24
34
|
default:
|
|
25
35
|
throw new Error(\`Paso desconocido: \${context.step}\`);
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
}
|
|
29
|
-
}`;
|
|
30
|
-
${
|
|
39
|
+
}`;n.addVariableStatement({isExported:!0,declarationKind:_.Const,declarations:[{name:e,type:`DurableFunction<${b}, ${p}, ${v}, ${I}>`,initializer:V}]}),n.organizeImports()}function A(e,t){if(e.length===0){let u=[];if(t.pendingStateAssignment){let y=`case ${t.step}: {
|
|
40
|
+
${t.pendingStateAssignment}
|
|
31
41
|
return { type: 'COMPLETE', result: undefined };
|
|
32
|
-
}`;
|
|
33
|
-
`),
|
|
34
|
-
return { type: 'COMPLETE', result: undefined };`;return{clauses:[`case ${
|
|
35
|
-
${
|
|
36
|
-
}`],nextStep:
|
|
37
|
-
`),
|
|
38
|
-
${
|
|
39
|
-
}`,
|
|
40
|
-
`),
|
|
42
|
+
}`;u.push(y)}return{clauses:u,nextStep:t.step+1}}let{syncBlock:o,durableStatement:n,nextStatements:i}=de(e),{rewrittenSyncStatements:c,newlyPersistedVariables:l}=ce(o,n?[n,...i]:[],t.persistedVariables);t.pendingStateAssignment&&c.unshift(t.pendingStateAssignment);let r=new Map([...t.persistedVariables,...l]);if(!n){let u=c.join(`
|
|
43
|
+
`),f=o.length>0&&a.isReturnStatement(o[o.length-1])?"":`
|
|
44
|
+
return { type: 'COMPLETE', result: undefined };`;return{clauses:[`case ${t.step}: {
|
|
45
|
+
${u}${f}
|
|
46
|
+
}`],nextStep:t.step+1}}if(a.isIfStatement(n))return ae(n,i,{...t,persistedVariables:r},c);if(a.isTryStatement(n))return ie(n,i,{...t,persistedVariables:r},c);let{instruction:g,nextPendingStateAssignment:d}=pe(n,r);c.push(g);let s=c.join(`
|
|
47
|
+
`),S=`case ${t.step}: {
|
|
48
|
+
${s}
|
|
49
|
+
}`,m={step:t.step+1,persistedVariables:r,pendingStateAssignment:d},p=A(i,m);return{clauses:[S,...p.clauses],nextStep:p.nextStep}}function ae(e,t,o,n){let i=k(e.getExpression(),o.persistedVariables),c=e.getThenStatement(),l=a.isBlock(c)?c.getStatements():[c],r=A(l,{step:o.step+1,persistedVariables:new Map(o.persistedVariables)}),g,d=e.getElseStatement();if(d){let y=a.isBlock(d)?d.getStatements():[d];g=A(y,{step:r.nextStep,persistedVariables:new Map(o.persistedVariables)})}let s=g?g.nextStep:r.nextStep,S=A(t,{step:s,persistedVariables:o.persistedVariables}),m=n.join(`
|
|
50
|
+
`),p=r.nextStep;return{clauses:[`
|
|
41
51
|
case ${o.step}: {
|
|
42
|
-
${
|
|
43
|
-
if (${
|
|
52
|
+
${m}
|
|
53
|
+
if (${i}) {
|
|
44
54
|
context.step = ${o.step+1};
|
|
45
55
|
} else {
|
|
46
|
-
${
|
|
56
|
+
${d?`context.step = ${p};`:`context.step = ${s};`}
|
|
47
57
|
}
|
|
48
58
|
break;
|
|
49
59
|
}
|
|
50
|
-
`,...r.clauses,...
|
|
51
|
-
case ${
|
|
52
|
-
${
|
|
60
|
+
`,...r.clauses,...g?g.clauses:[],...S.clauses],nextStep:S.nextStep}}function ie(e,t,o,n){let{step:i,persistedVariables:c}=o,l=e.getTryBlock(),r=e.getCatchClause(),g=e.getFinallyBlock(),d=A(l.getStatements(),{step:i+1,persistedVariables:new Map(c)}),s,S,m=d.nextStep;if(r){let w=r.getBlock(),h=r.getVariableDeclaration();h&&(S=h.getName()),s=A(w.getStatements(),{step:m,persistedVariables:new Map(c)})}let p,u=s?s.nextStep:m;g&&(p=A(g.getStatements(),{step:u,persistedVariables:new Map(c)}));let y=p?p.nextStep:u,f=A(t,{step:y,persistedVariables:c}),b=`{ catchStep: ${r?m:"undefined"}, finallyStep: ${g?u:"undefined"} }`,v=`
|
|
61
|
+
case ${i}: {
|
|
62
|
+
${n.join(`
|
|
53
63
|
`)}
|
|
54
64
|
state.tryCatchStack = state.tryCatchStack || [];
|
|
55
|
-
state.tryCatchStack.push(${
|
|
56
|
-
context.step = ${
|
|
65
|
+
state.tryCatchStack.push(${b});
|
|
66
|
+
context.step = ${i+1}; // Salta al inicio del bloque try
|
|
57
67
|
break;
|
|
58
68
|
}
|
|
59
|
-
`,I=
|
|
60
|
-
const ${
|
|
61
|
-
Please split them into separate lines/variables to ensure safe state persistence.`);let
|
|
69
|
+
`,I=d.clauses.pop()||"",$=g?u:y;if(d.clauses.push(I.replace(/return { type: 'COMPLETE'.* };/,`context.step = ${$}; break;`)),s){if(S){let h=s.clauses[0]||`case ${m}: {}`;s.clauses[0]=h.replace("{",`{
|
|
70
|
+
const ${S} = result as unknown;`)}let w=s.clauses.pop()||"";s.clauses.push(w.replace(/return { type: 'COMPLETE'.* };/,`context.step = ${$}; break;`))}if(p){let w=p.clauses.pop()||"";p.clauses.push(w.replace(/return { type: 'COMPLETE'.* };/,`state.tryCatchStack?.pop(); context.step = ${y}; break;`))}return{clauses:[v,...d.clauses,...s?s.clauses:[],...p?p.clauses:[],...f.clauses],nextStep:f.nextStep}}function ce(e,t,o){let n=[],i=new Map,c=ue(t),l=new Map(o);for(let r of e){if(a.isVariableStatement(r))for(let d of r.getDeclarations()){let s=d.getInitializer();if(!s)continue;let S=le(d),m=S.filter(p=>c.has(p.name));if(m.length>0){let p=k(s,o);for(let{name:u,type:y}of m){i.set(u,{type:y}),l.set(u,{type:y});let f=S.length>1?`${p}.${u}`:p;n.push(`state.${u} = ${f};`)}}}n.push(k(r,l))}return{rewrittenSyncStatements:n,newlyPersistedVariables:i}}function le(e){let t=e.getNameNode(),o=[];if(a.isIdentifier(t)){let n=e.getType().getText(e,L);o.push({name:t.getText(),type:n})}else if(a.isObjectBindingPattern(t))for(let n of t.getElements()){let i=n.getName(),c=n.getType().getText(n,L);o.push({name:i,type:c})}return o}function pe(e,t){if(a.isReturnStatement(e))return{instruction:`return { type: 'COMPLETE', result: ${e.getExpression()?k(e.getExpressionOrThrow(),t):"undefined"} };`,nextPendingStateAssignment:void 0};if(e.getDescendantsOfKind(P.AwaitExpression).length>1)throw new Error(`[b-durable Compiler Error] Multiple 'await' expressions found in a single statement at line ${e.getStartLineNumber()}.
|
|
71
|
+
Please split them into separate lines/variables to ensure safe state persistence.`);let n,i=e.getFirstDescendantByKind(P.VariableDeclaration);if(i){let l=i.getName(),r=i.getType().getText(i,L);t.set(l,{type:r}),n=`state.${l} = result;`}let c=e.getFirstDescendantByKind(P.AwaitExpression);if(c){let l=c.getParent();if(!(a.isExpressionStatement(l)||a.isVariableDeclaration(l)||a.isCallExpression(l)&&a.isExpressionStatement(l.getParent()))&&i&&i.getInitializer()!==c)throw new Error(`[b-durable Compiler Error] Complex 'await' usage detected at line ${e.getStartLineNumber()}.
|
|
62
72
|
The 'await' keyword must be the direct value of the assignment.
|
|
63
73
|
Invalid: const x = 1 + await foo();
|
|
64
|
-
Valid: const temp = await foo(); const x = 1 + temp;`);let
|
|
74
|
+
Valid: const temp = await foo(); const x = 1 + temp;`);let g=c.getExpression();if(a.isCallExpression(g))return{instruction:`return ${ge(g,t)};`,nextPendingStateAssignment:n}}return{instruction:k(e,t),nextPendingStateAssignment:n}}function ue(e){let t=new Set;for(let o of e)o.getDescendantsOfKind(P.Identifier).forEach(n=>{t.add(n.getText())});return t}function k(e,t){let o=e.getText(),n=e.getStart(),i=[],c=e.getDescendantsOfKind(P.Identifier);a.isIdentifier(e)&&c.push(e),c.forEach(r=>{let g=r.getText();if(!t.has(g))return;let d=r.getSymbol();if(d&&d.getDeclarations().some(x=>x.getStart()>=e.getStart()&&x.getEnd()<=e.getEnd()))return;let s=r.getParent(),S=a.isVariableDeclaration(s)&&s.getNameNode()===r,m=a.isPropertyAccessExpression(s)&&s.getNameNode()===r||a.isPropertyAssignment(s)&&s.getNameNode()===r,p=a.isBindingElement(s)&&s.getNameNode()===r,u=a.isShorthandPropertyAssignment(s)&&s.getNameNode()===r,y=a.isParameterDeclaration(s)&&s.getNameNode()===r,f=a.isFunctionDeclaration(s)&&s.getNameNode()===r,b=a.isClassDeclaration(s)&&s.getNameNode()===r,v=a.isInterfaceDeclaration(s)&&s.getNameNode()===r,I=a.isTypeAliasDeclaration(s)&&s.getNameNode()===r,$=a.isEnumDeclaration(s)&&s.getNameNode()===r,w=a.isMethodDeclaration(s)&&s.getNameNode()===r;if(!S&&!m&&!p&&!y&&!f&&!b&&!v&&!I&&!$&&!w){let h=t.get(g),V=r.getStart()-n,x=r.getEnd()-n,E=`(state.${g} as ${h.type})`;u&&(E=`${g}: ${E}`),i.push({start:V,end:x,text:E})}}),i.sort((r,g)=>g.start-r.start);let l=o;for(let{start:r,end:g,text:d}of i)l=l.substring(0,r)+d+l.substring(g);return l}function ge(e,t){let o=e.getExpression(),n,i=!1;a.isPropertyAccessExpression(o)?(o.getExpression().getText()==="context"&&(i=!0),n=o.getName()):n=o.getText();let c=e.getArguments().map(s=>k(s,t)).join(", ");if(i)switch(n){case"bSleep":return`{ type: 'SCHEDULE_SLEEP', duration: ${c} }`;case"bWaitForEvent":return`{ type: 'WAIT_FOR_SIGNAL', signalName: ${c} }`;case"bExecute":{let[s,S]=e.getArguments(),m=s.getText(),p=S?k(S,t):"undefined";return`{ type: 'EXECUTE_SUBWORKFLOW', workflowName: ${m}.name, input: ${p} }`}case"bSignal":{let[s,S]=e.getArguments().map(m=>k(m,t));return`{ type: 'EMIT_EVENT', eventName: ${s}, payload: ${S} }`}default:throw new Error(`Funci\xF3n de contexto durable desconocida: '${n}'.`)}let l=o.getSymbol();if(!l)throw new Error(`S\xEDmbolo no encontrado para '${n}'.`);let r=l.getDeclarations()[0]?.asKind(P.ImportSpecifier);if(!r)throw new Error(`'${n}' debe ser importada.`);let g=r.getImportDeclaration().getModuleSpecifierSourceFileOrThrow();return`{ type: 'SCHEDULE_TASK', modulePath: '${N.relative(process.cwd(),g.getFilePath()).replace(/\\/g,"/")}', exportName: '${n}', args: [${c}] }`}function O(e){for(let t of e.getDescendantsOfKind(P.AwaitExpression)){let o=t.getExpressionIfKind(P.CallExpression);if(o){let n=o.getExpression();if(a.isPropertyAccessExpression(n)){let i=n.getName();if(n.getExpression().getText()==="context"&&(i==="bSleep"||i==="bWaitForEvent"||i==="bExecute"||i==="bSignal")||n.getSymbol()?.getDeclarations()[0]?.isKind(P.ImportSpecifier))return!0}else if(n.getSymbol()?.getDeclarations()[0]?.isKind(P.ImportSpecifier))return!0}}if(a.isTryStatement(e)&&(O(e.getTryBlock())||e.getCatchClause()&&O(e.getCatchClause().getBlock())||e.getFinallyBlock()&&O(e.getFinallyBlock())))return!0;if(a.isIfStatement(e)){let t=O(e.getThenStatement()),o=e.getElseStatement()?O(e.getElseStatement()):!1;return t||o}return a.isBlock(e)?e.getStatements().some(O):!1}function de(e){for(let t=0;t<e.length;t++){let o=e[t];if(a.isReturnStatement(o)||O(o)||a.isTryStatement(o))return{syncBlock:e.slice(0,t),durableStatement:o,nextStatements:e.slice(t+1)}}return{syncBlock:e,durableStatement:null,nextStatements:[]}}var G=e=>{let t=process.argv.indexOf(e);if(t!==-1&&process.argv.length>t+1)return process.argv[t+1]};async function me(){let e=G("--in"),t=G("--out"),n=process.argv.slice(2).includes("dev")?"dev":"prod";(!e||!t)&&(console.error("Uso: b-durable-compiler <dev|prod> --in <src> --out <generated>"),process.exit(1));let i=q.resolve(process.cwd(),e),c=q.resolve(process.cwd(),t);await H({inputDir:i,outputDir:c,packageName:"@bobtail.software/b-durable",mode:n})}me().catch(e=>{console.error("Error durante la compilaci\xF3n:",e),process.exit(1)});
|
package/package.json
CHANGED