@bobtail.software/b-durable 1.0.0 → 1.0.1
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/index.mjs +1 -1
- package/package.json +2 -4
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var n,u;function k(i){if(n||u){console.warn("[Persistence] Los clientes de Redis ya han sido configurados. Omitiendo.");return}n=i.commandClient,u=i.blockingClient}import E
|
|
1
|
+
var n,u;function k(i){if(n||u){console.warn("[Persistence] Los clientes de Redis ya han sido configurados. Omitiendo.");return}n=i.commandClient,u=i.blockingClient}import{randomUUID as E}from"crypto";import h from"ms";import{resolve as I}from"path";var d="queue:tasks",c="durable:sleepers";var p={RUNNING:"RUNNING",SLEEPING:"SLEEPING",COMPLETED:"COMPLETED",FAILED:"FAILED"};var w=class{durableFns=new Map;isWorkerRunning=!1;isSchedulerRunning=!1;schedulerInterval=null;sourceRoot;constructor(t){this.sourceRoot=t.sourceRoot}async start(t,a){let o=E();console.log(`[RUNTIME] Iniciando workflow '${t.name}' con ID: ${o}`);let e={workflowId:o,name:t.name,status:p.RUNNING,step:"0",input:JSON.stringify(a),state:JSON.stringify({})};await n.hset(`workflow:${o}`,e),await this.executeStep(o,t)}async executeStep(t,a,o){let e=await n.hgetall(`workflow:${t}`);if(!e||e.status!=="RUNNING")return;let r={workflowId:t,step:parseInt(e.step,10),input:JSON.parse(e.input),state:JSON.parse(e.state),result:o,log:s=>{console.log(`[WF:${t}] ${s}`)}};try{let s=await a.execute(r);await this.handleInstruction(s,r)}catch(s){console.error(`[RUNTIME] Error en workflow ${t} en el paso ${r.step}:`,s);let l={status:p.FAILED,error:s instanceof Error?s.message:String(s)};await n.hset(`workflow:${t}`,l)}}async handleInstruction(t,a){let{workflowId:o,state:e}=a;switch(await n.hset(`workflow:${o}`,"state",JSON.stringify(e)),t.type){case"SCHEDULE_TASK":{console.log(`[RUNTIME] Workflow ${o} agend\xF3 la tarea '${t.exportName}'.`);let r={workflowId:o,durableFunctionName:await n.hget(`workflow:${o}`,"name"),modulePath:t.modulePath,exportName:t.exportName,args:t.args};await n.lpush(d,JSON.stringify(r));break}case"SCHEDULE_SLEEP":{let r=h(t.duration),s=Date.now()+r;console.log(`[RUNTIME] Workflow ${o} en pausa por ${t.duration}. Despertar\xE1 en ${new Date(s).toISOString()}`),await n.hset(`workflow:${o}`,"status","SLEEPING"),await n.zadd(c,s.toString(),o);break}case"COMPLETE":{console.log(`[RUNTIME] Workflow ${o} completado.`);let r={status:"COMPLETED",result:JSON.stringify(t.result),step:(a.step+1).toString()};await n.hset(`workflow:${o}`,r);break}}}startScheduler(){if(this.isSchedulerRunning)return;this.isSchedulerRunning=!0,console.log("[SCHEDULER] Scheduler iniciado.");let t=async()=>{let a=Date.now(),o=await n.zrangebyscore(c,0,a);if(o.length>0){console.log(`[SCHEDULER] Despertando ${o.length} workflow(s)...`),await n.zrem(c,...o);for(let e of o){let r=await n.hget(`workflow:${e}`,"name");if(!r){console.error(`[SCHEDULER] No se pudo encontrar el nombre del workflow para el ID ${e}. Saltando.`);continue}let s=this.durableFns.get(r);if(!s){console.error(`[SCHEDULER] El workflow '${r}' (ID: ${e}) no est\xE1 registrado en este worker. Saltando.`);continue}console.log(`[SCHEDULER] Reanudando workflow ${e}`),await n.hset(`workflow:${e}`,"status","RUNNING"),await n.hincrby(`workflow:${e}`,"step",1),await this.executeStep(e,s,null)}}};this.schedulerInterval=setInterval(t,2e3)}startWorker(){if(this.isWorkerRunning)return;this.isWorkerRunning=!0,console.log("[WORKER] Worker iniciado, esperando tareas..."),(async()=>{for(;this.isWorkerRunning;)try{let a=await u.brpop(d,0);if(a){let[,o]=a,e=JSON.parse(o);console.log(`[WORKER] Tarea recibida: ${e.exportName}`);try{let r=e.modulePath.startsWith("virtual:")?e.modulePath:I(this.sourceRoot,e.modulePath);console.log(`[WORKER] Importando m\xF3dulo desde: ${r}`);let l=(await import(r))[e.exportName];if(typeof l!="function")throw new Error(`La exportaci\xF3n '${e.exportName}' no es una funci\xF3n en '${e.modulePath}'.`);let m=await l(...e.args),g=`workflow:${e.workflowId}`;await n.hincrby(g,"step",1);let f=this.durableFns.get(e.durableFunctionName);f&&await this.executeStep(e.workflowId,f,m)}catch(r){console.error(`[WORKER] Falla en la tarea '${e.exportName}' para el workflow ${e.workflowId}. Marcando como FAILED.`);let s={status:p.FAILED,error:r instanceof Error?r.message:String(r)};await n.hset(`workflow:${e.workflowId}`,s)}}}catch(a){if(a instanceof Error&&a.message.includes("Connection is closed")){console.log("[WORKER] Conexi\xF3n de bloqueo cerrada.");break}console.error("[WORKER] Error de infraestructura:",a)}})()}run(t){this.durableFns=t,this.startWorker(),this.startScheduler()}stop(){this.isWorkerRunning=!1,this.isSchedulerRunning=!1,this.schedulerInterval&&clearInterval(this.schedulerInterval),console.log("[RUNTIME] Solicitando detenci\xF3n...")}};var R=i=>i;function S(i){throw new Error(`The "sleep" function can only be called inside a "bDurable" workflow. ${i}`)}function U(i){console.log("--- Inicializando Sistema Durable ---"),k({commandClient:i.redisClient,blockingClient:i.blockingRedisClient});let t=new w({sourceRoot:i.sourceRoot});return t.run(i.durableFunctions),{start:t.start.bind(t),stop:t.stop.bind(t),runtime:t}}export{R as bDurable,U as bDurableInitialize,S as sleep};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobtail.software/b-durable",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"main": "dist/index.mjs",
|
|
5
5
|
"types": "dist/index.d.mts",
|
|
6
6
|
"description": "A system for creating durable, resilient, and type-safe workflows in JavaScript/TypeScript.",
|
|
@@ -23,13 +23,11 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"ioredis": "^5.8.2",
|
|
25
25
|
"ms": "^2.1.3",
|
|
26
|
-
"ts-morph": "^27.0.2"
|
|
27
|
-
"uuid": "^13.0.0"
|
|
26
|
+
"ts-morph": "^27.0.2"
|
|
28
27
|
},
|
|
29
28
|
"devDependencies": {
|
|
30
29
|
"@types/ms": "^2.1.0",
|
|
31
30
|
"@types/node": "^24.10.0",
|
|
32
|
-
"@types/uuid": "^11.0.0",
|
|
33
31
|
"tsup": "^8.5.0",
|
|
34
32
|
"typescript": "^5.9.3",
|
|
35
33
|
"vitest": "^4.0.7"
|