@bobtail.software/b-durable 1.0.5 → 1.0.7

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.d.mts CHANGED
@@ -1,6 +1,29 @@
1
1
  import Redis from 'ioredis';
2
2
  import ms from 'ms';
3
3
 
4
+ interface Logger {
5
+ info(message: string, meta?: Record<string, unknown>): void;
6
+ error(message: string, meta?: Record<string, unknown>): void;
7
+ warn(message: string, meta?: Record<string, unknown>): void;
8
+ debug(message: string, meta?: Record<string, unknown>): void;
9
+ }
10
+ interface WorkflowStateInfo<TInput = unknown, TOutput = unknown> {
11
+ workflowId: string;
12
+ name: string;
13
+ version: string;
14
+ status: string;
15
+ step: number;
16
+ input: TInput;
17
+ output?: TOutput;
18
+ state: Record<string, unknown>;
19
+ error?: string;
20
+ createdAt?: number;
21
+ updatedAt?: number;
22
+ pendingTask?: {
23
+ name: string;
24
+ attempts: number;
25
+ };
26
+ }
4
27
  interface WorkflowState {
5
28
  tryCatchStack?: {
6
29
  catchStep?: number;
@@ -14,7 +37,17 @@ interface WorkflowContext<TInput = unknown> {
14
37
  input: TInput;
15
38
  state: WorkflowState;
16
39
  result?: unknown;
17
- log: (message: string) => void;
40
+ log: (message: string, meta?: Record<string, unknown>) => void;
41
+ }
42
+ interface RetryOptions {
43
+ /** Número máximo de intentos. Infinity para reintentar siempre. Por defecto: 3 */
44
+ maxAttempts?: number;
45
+ /** Tiempo de espera antes del primer reintento (ej: '1s'). Por defecto: '1s' */
46
+ initialInterval?: ms.StringValue;
47
+ /** Coeficiente de multiplicación para backoff exponencial. Por defecto: 2 */
48
+ backoffCoefficient?: number;
49
+ /** Tiempo máximo de espera entre reintentos (ej: '1h'). Por defecto: '1h' */
50
+ maxInterval?: ms.StringValue;
18
51
  }
19
52
  type Instruction<TOutput = unknown> = {
20
53
  type: 'SCHEDULE_TASK';
@@ -25,55 +58,147 @@ type Instruction<TOutput = unknown> = {
25
58
  type: 'SCHEDULE_SLEEP';
26
59
  duration: ms.StringValue;
27
60
  } | {
28
- type: 'WAIT_FOR_EVENT';
29
- eventName: string;
61
+ type: 'WAIT_FOR_SIGNAL';
62
+ signalName: string;
30
63
  } | {
31
64
  type: 'EXECUTE_SUBWORKFLOW';
32
65
  workflowName: string;
33
66
  input: unknown;
67
+ } | {
68
+ type: 'EMIT_EVENT';
69
+ eventName: string;
70
+ payload: unknown;
34
71
  } | {
35
72
  type: 'COMPLETE';
36
73
  result: TOutput;
37
74
  };
38
- interface DurableFunction<TInput = unknown, TOutput = unknown, TEvents = Record<string, never>> {
75
+ interface DurableFunction<TInput = unknown, TOutput = unknown, TSignals = Record<string, never>, // INPUT: Señales que el workflow puede RECIBIR
76
+ TEvents = Record<string, never>> {
39
77
  __isDurable: true;
40
78
  name: string;
79
+ version: string;
80
+ retryOptions?: RetryOptions;
41
81
  execute: (context: WorkflowContext<TInput>) => Promise<Instruction<TOutput>>;
82
+ _TSignals?: TSignals;
42
83
  _TEvents?: TEvents;
43
84
  }
44
-
45
- interface StartOptions<TInput> {
85
+ /**
86
+ * Transforma un mapa de eventos en props de listeners.
87
+ * Ejemplo: { started: { id: string } } => { onStarted?: (payload: { id: string }) => void }
88
+ */
89
+ type WorkflowEventListeners<TEvents> = {
90
+ [K in keyof TEvents as `on${Capitalize<string & K>}`]?: (payload: TEvents[K]) => void;
91
+ };
92
+ /**
93
+ * Representa cualquier evento que sale del workflow, ya sea definido por el usuario
94
+ * o por el sistema (completado/fallido).
95
+ */
96
+ type WorkflowEvent<TEvents, TOutput> = {
97
+ [K in keyof TEvents]: {
98
+ name: K;
99
+ payload: TEvents[K];
100
+ };
101
+ }[keyof TEvents] | {
102
+ name: 'workflow:completed';
103
+ payload: TOutput;
104
+ } | {
105
+ name: 'workflow:failed';
106
+ payload: {
107
+ message: string;
108
+ };
109
+ };
110
+ /**
111
+ * Utilidad para "aplanar" intersecciones de tipos.
112
+ * Esto fuerza a TypeScript a computar el objeto final inmediatamente,
113
+ * permitiendo que la inferencia contextual de los argumentos funcione correctamente.
114
+ */
115
+ type Compute<T> = {
116
+ [K in keyof T]: T[K];
117
+ } & unknown;
118
+ /**
119
+ * Opciones para iniciar el workflow. Combina input estándar + listeners dinámicos.
120
+ */
121
+ type StartOptions<TInput, TEvents> = Compute<{
46
122
  input: TInput;
47
123
  workflowId?: string;
124
+ } & WorkflowEventListeners<TEvents>>;
125
+ interface StartedWorkflowHandle {
126
+ workflowId: string;
127
+ unsubscribe: () => Promise<void>;
128
+ }
129
+ /**
130
+ * Handle para interactuar con un workflow en ejecución.
131
+ */
132
+ interface WorkflowHandle<TSignals, TEvents, TOutput> {
133
+ workflowId: string;
134
+ /**
135
+ * Envía una señal (INPUT) al workflow.
136
+ * @param name Nombre de la señal (definido en TSignals).
137
+ * @param payload Datos de la señal.
138
+ */
139
+ signal: <K extends keyof TSignals>(name: K, payload: TSignals[K]) => Promise<void>;
140
+ /**
141
+ * Suscribe un callback a un evento específico (OUTPUT).
142
+ * @param eventName Nombre del evento (definido en TEvents).
143
+ * @param handler Función que recibe el payload.
144
+ */
145
+ on: <K extends keyof TEvents>(eventName: K, handler: (payload: TEvents[K]) => void) => Promise<{
146
+ unsubscribe: () => void;
147
+ }>;
148
+ /**
149
+ * Suscribe un callback a TODOS los eventos (incluyendo sistema).
150
+ * Útil para logging o debug.
151
+ */
152
+ subscribe: (handler: (event: WorkflowEvent<TEvents, TOutput>) => void) => Promise<{
153
+ unsubscribe: () => void;
154
+ }>;
48
155
  }
156
+ declare class WorkflowCancellationError extends Error {
157
+ readonly isCancellation = true;
158
+ constructor(message: string);
159
+ }
160
+
49
161
  declare class DurableRuntime {
50
162
  private durableFns;
51
163
  private repo;
52
164
  private workerId;
53
165
  private isRunning;
54
166
  private schedulerInterval;
167
+ private heartbeatInterval;
55
168
  private readonly sourceRoot;
169
+ private readonly pollingInterval;
170
+ private readonly logger;
171
+ private readonly maxTaskRetries;
56
172
  constructor(options: {
57
173
  sourceRoot: string;
174
+ retention?: ms.StringValue;
175
+ pollingInterval?: number;
176
+ logger?: Logger;
58
177
  });
59
- start<TInput, TOutput>(durableFn: DurableFunction<TInput, TOutput>, options: StartOptions<TInput>, parentId?: string): Promise<string>;
178
+ getState(workflowId: string): Promise<WorkflowStateInfo | null>;
179
+ start<TInput, TOutput, TSignals, TEvents>(durableFn: DurableFunction<TInput, TOutput, TSignals, TEvents>, options: StartOptions<TInput, TEvents>, parentId?: string): Promise<StartedWorkflowHandle>;
60
180
  private scheduleExecution;
61
181
  private _executeStep;
62
182
  private handleInstruction;
63
183
  private handleFailure;
64
184
  private resumeParentWorkflow;
65
185
  private propagateFailureToParent;
66
- sendEvent<T>(workflowId: string, eventName: string, payload: T): Promise<void>;
186
+ signal<T>(workflowId: string, signalName: string, payload: T): Promise<void>;
187
+ cancel(workflowId: string, reason: string): Promise<void>;
67
188
  private startScheduler;
189
+ private checkDelayedTasks;
190
+ private checkSleepers;
191
+ private reapDeadWorkers;
192
+ private startHeartbeat;
68
193
  private startWorker;
69
- run(durableFns: Map<string, DurableFunction<unknown>>): void;
70
- stop(): void;
194
+ run(durableFns: Map<string, DurableFunction<unknown, unknown, unknown, unknown>>): void;
195
+ stop(): Promise<void>;
71
196
  }
72
197
 
73
198
  /**
74
199
  * El contexto de ejecución proporcionado a cada workflow, con métodos de durabilidad tipados.
75
200
  */
76
- interface DurableContext<TEvents = Record<string, never>> extends Pick<WorkflowContext, 'log' | 'workflowId'> {
201
+ interface DurableContext<TEvents = Record<string, never>, TSignals = Record<string, never>> extends Pick<WorkflowContext, 'log' | 'workflowId'> {
77
202
  /**
78
203
  * Pausa la ejecución del workflow de manera duradera.
79
204
  * @param duration Una cadena de tiempo como '2 days', '10h', '7s'.
@@ -92,41 +217,64 @@ interface DurableContext<TEvents = Record<string, never>> extends Pick<WorkflowC
92
217
  * @param input La entrada para el sub-workflow.
93
218
  * @returns Una promesa que se resuelve con el resultado del sub-workflow.
94
219
  */
95
- bExecute<TInput, TOutput, TWorkflowEvents extends Record<string, any>>(workflow: DurableFunction<TInput, TOutput, TWorkflowEvents>, input: TInput): Promise<TOutput>;
220
+ bExecute<TInput, TOutput, TWorkflowEvents, TWorkflowSignals>(workflow: DurableFunction<TInput, TOutput, TWorkflowEvents, TWorkflowSignals>, input: TInput): Promise<TOutput>;
221
+ /**
222
+ * Emite una señal o notificación con un payload desde el workflow.
223
+ * Esta operación es duradera pero no bloquea la ejecución. El workflow continúa inmediatamente después.
224
+ * El nombre de la señal y el tipo del payload son validados contra el contrato de señales del workflow.
225
+ * @param signalName El nombre de la señal.
226
+ * @param payload Los datos a enviar con la señal.
227
+ */
228
+ bSignal<K extends keyof TSignals>(signalName: K, payload: TSignals[K]): Promise<void>;
96
229
  }
97
- type DurableWorkflowFn<TInput, TOutput, TEvents = Record<string, never>> = (input: TInput, context: DurableContext<TEvents>) => Promise<TOutput>;
98
- interface DurableWorkflowDef<TInput, TOutput, TEvents = Record<string, never>> {
230
+ type DurableWorkflowFn<TInput, TOutput, TEvents = Record<string, never>, TSignals = Record<string, never>> = (input: TInput, context: DurableContext<TEvents, TSignals>) => Promise<TOutput>;
231
+ interface DurableWorkflowDef<TInput, TOutput, TEvents = Record<string, never>, TSignals = Record<string, never>> {
99
232
  /**
100
233
  * La función async que contiene la lógica del workflow.
101
234
  */
102
- workflow: DurableWorkflowFn<TInput, TOutput, TEvents>;
235
+ workflow: DurableWorkflowFn<TInput, TOutput, TEvents, TSignals>;
236
+ /**
237
+ * Versión del workflow. Se utiliza para identificar versiones de un workflow.
238
+ */
239
+ version: string;
240
+ /**
241
+ * Política de reintento para las tareas ejecutadas por este workflow.
242
+ * Si falla una tarea, se aplicará esta configuración.
243
+ */
244
+ retryOptions?: RetryOptions;
103
245
  }
104
246
  /**
105
247
  * Marcador para que el compilador identifique y transforme una función en un workflow durable.
106
248
  * Esta función es un passthrough en tiempo de ejecución, su único propósito es para el análisis estático.
107
249
  */
108
- declare const bDurable: <TInput = any, TOutput = any, TEvents = Record<string, never>>(def: DurableWorkflowDef<TInput, TOutput, TEvents>) => DurableFunction<TInput, TOutput, TEvents>;
250
+ declare const bDurable: <TInput = any, TOutput = any, TEvents = Record<string, never>, TSignals = Record<string, never>>(def: DurableWorkflowDef<TInput, TOutput, TEvents, TSignals>) => DurableFunction<TInput, TOutput, TEvents, TSignals>;
109
251
 
110
252
  interface BDurableAPI {
111
- start: <TInput, TOutput>(durableFn: DurableFunction<TInput, TOutput>, options: StartOptions<TInput>) => Promise<string>;
253
+ /**
254
+ * Inicia un workflow durable.
255
+ * Permite definir listeners de eventos inmediatamente en las opciones (ej: { onProgress: ... })
256
+ * para evitar condiciones de carrera.
257
+ */
258
+ start: <TInput, TOutput, TSignals, TEvents>(durableFn: DurableFunction<TInput, TOutput, TSignals, TEvents>, options: StartOptions<TInput, TEvents>) => Promise<StartedWorkflowHandle>;
112
259
  stop: () => void;
113
260
  runtime: DurableRuntime;
261
+ cancel: (workflowId: string, reason: string) => Promise<void>;
262
+ getState: (workflowId: string) => Promise<WorkflowStateInfo | null>;
114
263
  /**
115
- * Envía un evento a un workflow en ejecución que está en pausa esperando dicho evento.
116
- * Esta función es estrictamente tipada basada en el tipo de eventos de la definición del workflow.
117
- * @param durableFn La definición del workflow al que se le enviará el evento. Se usa para la inferencia de tipos.
118
- * @param workflowId El ID del workflow al que se le enviará el evento.
119
- * @param eventName El nombre del evento. Será autocompletado por el editor.
120
- * @param payload La carga útil del evento. El tipo debe coincidir con el definido para `eventName`.
264
+ * Obtiene un "handle" para una instancia de workflow existente.
265
+ * Permite enviar señales (Input) y escuchar eventos (Output).
121
266
  */
122
- sendEvent: <TInput, TOutput, TWorkflowEvents, K extends keyof TWorkflowEvents>(durableFn: DurableFunction<TInput, TOutput, TWorkflowEvents>, workflowId: string, eventName: K, payload: TWorkflowEvents[K]) => Promise<void>;
267
+ getHandle: <TInput, TOutput, TSignals, TEvents>(durableFn: DurableFunction<TInput, TOutput, TSignals, TEvents>, workflowId: string) => WorkflowHandle<TSignals, TEvents, TOutput>;
123
268
  }
124
269
  interface InitializeOptions {
125
- durableFunctions: Map<string, DurableFunction<unknown, unknown>>;
270
+ durableFunctions: Map<string, DurableFunction<unknown, unknown, any, any>>;
126
271
  sourceRoot: string;
127
272
  redisClient: Redis;
128
273
  blockingRedisClient: Redis;
274
+ retention?: ms.StringValue;
275
+ pollingInterval?: number;
276
+ logger?: Logger;
129
277
  }
130
278
  declare function bDurableInitialize(options: InitializeOptions): BDurableAPI;
131
279
 
132
- export { type BDurableAPI, type DurableFunction, type Instruction, type StartOptions, type WorkflowContext, type WorkflowState, bDurable, bDurableInitialize };
280
+ export { type BDurableAPI, type DurableFunction, type Instruction, type Logger, type RetryOptions, type StartOptions, type StartedWorkflowHandle, WorkflowCancellationError, type WorkflowContext, type WorkflowEvent, type WorkflowEventListeners, type WorkflowHandle, type WorkflowState, type WorkflowStateInfo, bDurable, bDurableInitialize };