@gravito/flux 3.0.0 → 3.0.2

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 (48) hide show
  1. package/README.md +298 -0
  2. package/bin/flux.js +25 -1
  3. package/dev/viewer/app.js +4 -4
  4. package/dist/bun.cjs +2 -2
  5. package/dist/bun.cjs.map +1 -1
  6. package/dist/bun.d.cts +65 -26
  7. package/dist/bun.d.ts +65 -26
  8. package/dist/bun.js +1 -1
  9. package/dist/chunk-4DXCQ6CL.js +3486 -0
  10. package/dist/chunk-4DXCQ6CL.js.map +1 -0
  11. package/dist/chunk-6AZNHVEO.cjs +316 -0
  12. package/dist/chunk-6AZNHVEO.cjs.map +1 -0
  13. package/dist/{chunk-ZAMVC732.js → chunk-NAIVO7RR.js} +64 -15
  14. package/dist/chunk-NAIVO7RR.js.map +1 -0
  15. package/dist/chunk-WAPZDXSX.cjs +3486 -0
  16. package/dist/chunk-WAPZDXSX.cjs.map +1 -0
  17. package/dist/chunk-WGDTB6OC.js +316 -0
  18. package/dist/chunk-WGDTB6OC.js.map +1 -0
  19. package/dist/{chunk-SJSPR4ZU.cjs → chunk-YXBEYVGY.cjs} +66 -17
  20. package/dist/chunk-YXBEYVGY.cjs.map +1 -0
  21. package/dist/cli/flux-visualize.cjs +108 -0
  22. package/dist/cli/flux-visualize.cjs.map +1 -0
  23. package/dist/cli/flux-visualize.d.cts +1 -0
  24. package/dist/cli/flux-visualize.d.ts +1 -0
  25. package/dist/cli/flux-visualize.js +108 -0
  26. package/dist/cli/flux-visualize.js.map +1 -0
  27. package/dist/index.cjs +100 -12
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +402 -12
  30. package/dist/index.d.ts +402 -12
  31. package/dist/index.js +98 -10
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.node.cjs +11 -3
  34. package/dist/index.node.cjs.map +1 -1
  35. package/dist/index.node.d.cts +1114 -258
  36. package/dist/index.node.d.ts +1114 -258
  37. package/dist/index.node.js +10 -2
  38. package/dist/types-CRz5XdLd.d.cts +433 -0
  39. package/dist/types-CRz5XdLd.d.ts +433 -0
  40. package/package.json +17 -6
  41. package/dist/chunk-LULCFPIK.js +0 -1004
  42. package/dist/chunk-LULCFPIK.js.map +0 -1
  43. package/dist/chunk-SJSPR4ZU.cjs.map +0 -1
  44. package/dist/chunk-X3NC7HS4.cjs +0 -1004
  45. package/dist/chunk-X3NC7HS4.cjs.map +0 -1
  46. package/dist/chunk-ZAMVC732.js.map +0 -1
  47. package/dist/types-cnIU1O3n.d.cts +0 -250
  48. package/dist/types-cnIU1O3n.d.ts +0 -250
@@ -0,0 +1,433 @@
1
+ /**
2
+ * @fileoverview Lock Provider interface for distributed execution
3
+ *
4
+ * Provides a mechanism for coordinating workflow execution across multiple
5
+ * nodes by ensuring only one node executes a specific workflow instance at a time.
6
+ *
7
+ * @module @gravito/flux/core
8
+ */
9
+ /**
10
+ * Represents a distributed lock for a specific resource.
11
+ */
12
+ interface Lock {
13
+ /** The unique identifier for the locked resource. */
14
+ id: string;
15
+ /** The owner of the lock (e.g., node identifier). */
16
+ owner: string;
17
+ /** Unix timestamp when the lock expires. */
18
+ expiresAt: number;
19
+ /** Releases the lock manually. */
20
+ release(): Promise<void>;
21
+ }
22
+ /**
23
+ * Interface for implementing distributed locking mechanisms.
24
+ * Required for Cluster Mode to prevent race conditions in multi-node environments.
25
+ */
26
+ interface LockProvider {
27
+ /**
28
+ * Attempts to acquire a lock for a specific resource.
29
+ *
30
+ * @param resourceId - The unique ID of the resource to lock (e.g., workflowId).
31
+ * @param owner - The identifier of the node/process requesting the lock.
32
+ * @param ttl - Time-to-live for the lock in milliseconds.
33
+ * @returns A Lock object if successful, otherwise null.
34
+ */
35
+ acquire(resourceId: string, owner: string, ttl: number): Promise<Lock | null>;
36
+ /**
37
+ * Refreshes an existing lock to extend its lifetime.
38
+ *
39
+ * @param resourceId - The ID of the resource.
40
+ * @param owner - The current owner of the lock.
41
+ * @param ttl - The new time-to-live from the current moment.
42
+ * @returns True if the lock was successfully refreshed.
43
+ */
44
+ refresh(resourceId: string, owner: string, ttl: number): Promise<boolean>;
45
+ /**
46
+ * Forcefully releases a lock, regardless of the owner.
47
+ *
48
+ * @param resourceId - The ID of the resource to unlock.
49
+ */
50
+ release(resourceId: string): Promise<void>;
51
+ }
52
+ /**
53
+ * In-memory implementation of LockProvider for local development and testing.
54
+ * Does NOT support multiple processes or nodes.
55
+ */
56
+ declare class MemoryLockProvider implements LockProvider {
57
+ private locks;
58
+ acquire(resourceId: string, owner: string, ttl: number): Promise<Lock | null>;
59
+ refresh(resourceId: string, owner: string, ttl: number): Promise<boolean>;
60
+ release(resourceId: string): Promise<void>;
61
+ private createLock;
62
+ }
63
+
64
+ /**
65
+ * Represents the current lifecycle state of a workflow instance.
66
+ * Used to track progress and determine valid state transitions.
67
+ */
68
+ type WorkflowStatus = 'pending' | 'running' | 'paused' | 'completed' | 'failed' | 'suspended' | 'rolling_back' | 'rolled_back' | 'compensation_failed';
69
+ /**
70
+ * Signal payload returned by a step to pause execution until an external event occurs.
71
+ * This enables long-running workflows that wait for human approval or webhooks.
72
+ */
73
+ interface FluxWaitResult {
74
+ __kind: 'flux_wait';
75
+ /** The unique identifier for the signal this workflow is waiting for. */
76
+ signal: string;
77
+ }
78
+ /**
79
+ * The possible return values from a step handler.
80
+ * Supports synchronous results, promises, and suspension signals.
81
+ */
82
+ type StepHandlerResult = undefined | undefined | FluxWaitResult | Promise<undefined | undefined | FluxWaitResult>;
83
+ /**
84
+ * Internal representation of a step's execution outcome.
85
+ * Captures performance metrics and state for persistence and observability.
86
+ */
87
+ interface StepResult<T = unknown> {
88
+ /** Indicates if the step completed without unhandled exceptions. */
89
+ success: boolean;
90
+ /** The data produced by the step, if any. */
91
+ data?: T;
92
+ /** The error encountered during execution, if success is false. */
93
+ error?: Error;
94
+ /** Execution time in milliseconds. */
95
+ duration: number;
96
+ /** Whether the step requested workflow suspension. */
97
+ suspended?: boolean;
98
+ /** The signal name if the step is suspended. */
99
+ waitingFor?: string;
100
+ }
101
+ /**
102
+ * Audit log entry for a single step execution within a workflow.
103
+ * Provides a historical record for debugging and compliance.
104
+ */
105
+ interface StepExecution {
106
+ /** The unique name of the step as defined in the workflow. */
107
+ name: string;
108
+ /** The execution outcome status. */
109
+ status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'suspended' | 'compensated' | 'compensating';
110
+ /** ISO timestamp when the step started. */
111
+ startedAt?: Date;
112
+ /** ISO timestamp when the step finished. */
113
+ completedAt?: Date;
114
+ /** ISO timestamp when the step was suspended. */
115
+ suspendedAt?: Date;
116
+ /** ISO timestamp when compensation logic finished. */
117
+ compensatedAt?: Date;
118
+ /** The signal name if currently suspended. */
119
+ waitingFor?: string;
120
+ /** Total execution time in milliseconds. */
121
+ duration?: number;
122
+ /** Serialized output data from the step. */
123
+ output?: any;
124
+ /** Error message if the step failed. */
125
+ error?: string;
126
+ /** Number of retry attempts performed. */
127
+ retries: number;
128
+ }
129
+ /**
130
+ * Configuration for a single unit of work within a workflow.
131
+ * Defines the business logic, error handling, and execution conditions.
132
+ */
133
+ interface StepDefinition<TInput = unknown, TData = Record<string, any>> {
134
+ /** Unique identifier for the step within the workflow. */
135
+ name: string;
136
+ /**
137
+ * The core business logic for this step.
138
+ * @param ctx - The current workflow execution context.
139
+ * @returns A result indicating completion or suspension.
140
+ */
141
+ handler: (ctx: WorkflowContext<TInput, TData>) => StepHandlerResult;
142
+ /**
143
+ * Logic to execute if a subsequent step fails, enabling the Saga pattern.
144
+ * @param ctx - The current workflow execution context.
145
+ */
146
+ compensate?: (ctx: WorkflowContext<TInput, TData>) => Promise<void> | void;
147
+ /** Maximum number of times to retry on failure before giving up. */
148
+ retries?: number;
149
+ /** Maximum execution time in milliseconds before timing out. */
150
+ timeout?: number;
151
+ /**
152
+ * Predicate to determine if this step should be executed.
153
+ * @param ctx - The current workflow execution context.
154
+ * @returns True if the step should run.
155
+ */
156
+ when?: (ctx: WorkflowContext<TInput, TData>) => boolean;
157
+ /** If true, the step is considered a side-effect that should not be re-run on replay. */
158
+ commit?: boolean;
159
+ /** If set, this step belongs to a parallel execution group with this ID. */
160
+ parallelGroup?: string;
161
+ }
162
+ /**
163
+ * Shared state and utilities provided to every step during execution.
164
+ * Acts as the "source of truth" for the current workflow run.
165
+ */
166
+ interface WorkflowContext<TInput = unknown, TData = Record<string, any>> {
167
+ /** Unique UUID for this specific workflow instance. */
168
+ readonly id: string;
169
+ /** The name of the workflow definition. */
170
+ readonly name: string;
171
+ /** Immutable input data provided when the workflow started. */
172
+ readonly input: TInput;
173
+ /** Mutable state shared across all steps in the workflow. */
174
+ data: TData;
175
+ /** Current lifecycle status of the workflow. */
176
+ readonly status: WorkflowStatus;
177
+ /** Zero-based index of the step currently being executed. */
178
+ readonly currentStep: number;
179
+ /** Full history of all steps executed so far. */
180
+ readonly history: StepExecution[];
181
+ /** Optimistic locking version to prevent concurrent modification issues. */
182
+ readonly version: number;
183
+ }
184
+ /**
185
+ * Persistable representation of a workflow's entire state.
186
+ * This structure is what storage adapters save and load.
187
+ */
188
+ interface WorkflowState<TInput = unknown, TData = Record<string, any>> {
189
+ /** Unique UUID for the workflow instance. */
190
+ id: string;
191
+ /** Name of the workflow definition. */
192
+ name: string;
193
+ /** Current lifecycle status. */
194
+ status: WorkflowStatus;
195
+ /** Original input data. */
196
+ input: TInput;
197
+ /** Current accumulated data. */
198
+ data: TData;
199
+ /** Index of the next step to execute. */
200
+ currentStep: number;
201
+ /** Audit trail of step executions. */
202
+ history: StepExecution[];
203
+ /** When the instance was first created. */
204
+ createdAt: Date;
205
+ /** When the instance was last modified. */
206
+ updatedAt: Date;
207
+ /** When the workflow reached a terminal state. */
208
+ completedAt?: Date;
209
+ /** Final error message if the workflow failed. */
210
+ error?: string;
211
+ /** Version number for concurrency control. */
212
+ version: number;
213
+ /** The version of the workflow definition used to create this instance */
214
+ definitionVersion?: string;
215
+ }
216
+ /**
217
+ * The blueprint for a workflow, containing its name and ordered steps.
218
+ */
219
+ interface WorkflowDefinition<TInput = unknown, TData = Record<string, any>> {
220
+ /** Unique name for this workflow type. */
221
+ name: string;
222
+ /** Semantic version of this workflow definition (e.g., "1.0.0", "2.1.0") */
223
+ version?: string;
224
+ /** Ordered list of steps to execute. */
225
+ steps: StepDefinition<TInput, TData>[];
226
+ /** Optional runtime validation for the workflow input. */
227
+ validateInput?: (input: unknown) => input is TInput;
228
+ }
229
+ /**
230
+ * Metadata describing a workflow's structure for UI or documentation.
231
+ */
232
+ interface WorkflowDescriptor {
233
+ /** Name of the workflow. */
234
+ name: string;
235
+ /** Semantic version of the workflow definition */
236
+ version?: string;
237
+ /** Metadata for each step in the workflow. */
238
+ steps: StepDescriptor[];
239
+ }
240
+ /**
241
+ * Metadata describing a single step's configuration.
242
+ */
243
+ interface StepDescriptor {
244
+ /** Name of the step. */
245
+ name: string;
246
+ /** Whether the step is marked as a commit. */
247
+ commit: boolean;
248
+ /** Configured retry limit. */
249
+ retries?: number;
250
+ /** Configured timeout in milliseconds. */
251
+ timeout?: number;
252
+ /** Whether the step has a conditional execution predicate. */
253
+ hasCondition: boolean;
254
+ }
255
+ /**
256
+ * Interface for implementing custom persistence layers.
257
+ * Allows workflows to survive process restarts and scale across nodes.
258
+ */
259
+ interface WorkflowStorage {
260
+ /**
261
+ * Persists the current state of a workflow.
262
+ * @param state - The state to save.
263
+ * @throws If the storage operation fails or version conflict occurs.
264
+ */
265
+ save(state: WorkflowState): Promise<void>;
266
+ /**
267
+ * Retrieves a workflow state by its unique ID.
268
+ * @param id - The workflow instance ID.
269
+ * @returns The state if found, otherwise null.
270
+ */
271
+ load(id: string): Promise<WorkflowState | null>;
272
+ /**
273
+ * Queries workflow instances based on filters.
274
+ * @param filter - Criteria for selecting workflows.
275
+ * @returns A list of matching workflow states.
276
+ */
277
+ list(filter?: WorkflowFilter): Promise<WorkflowState[]>;
278
+ /**
279
+ * Permanently removes a workflow instance from storage.
280
+ * @param id - The workflow instance ID.
281
+ */
282
+ delete(id: string): Promise<void>;
283
+ /** Optional initialization logic for the storage provider. */
284
+ init?(): Promise<void>;
285
+ /** Optional cleanup logic for the storage provider. */
286
+ close?(): Promise<void>;
287
+ }
288
+ /**
289
+ * Criteria for filtering workflow instances in storage queries.
290
+ */
291
+ interface WorkflowFilter {
292
+ /** Filter by workflow definition name. */
293
+ name?: string;
294
+ /** Filter by one or more lifecycle statuses. */
295
+ status?: WorkflowStatus | WorkflowStatus[];
296
+ /** Filter by definition version */
297
+ version?: string;
298
+ /** Maximum number of results to return. */
299
+ limit?: number;
300
+ /** Number of results to skip for pagination. */
301
+ offset?: number;
302
+ }
303
+ /**
304
+ * Generic logging interface for the Flux engine.
305
+ * Allows integration with various logging libraries (Pino, Winston, etc.).
306
+ */
307
+ interface FluxLogger {
308
+ debug(message: string, ...args: unknown[]): void;
309
+ info(message: string, ...args: unknown[]): void;
310
+ warn(message: string, ...args: unknown[]): void;
311
+ error(message: string, ...args: unknown[]): void;
312
+ }
313
+ /**
314
+ * Enumeration of all observable events emitted by the engine.
315
+ * Used for tracing, monitoring, and debugging.
316
+ */
317
+ type FluxTraceEventType = 'workflow:start' | 'workflow:complete' | 'workflow:error' | 'workflow:rollback_start' | 'workflow:rollback_complete' | 'step:start' | 'step:complete' | 'step:error' | 'step:skipped' | 'step:retry' | 'step:suspend' | 'step:compensate' | 'signal:received';
318
+ /**
319
+ * A single telemetry event captured during workflow execution.
320
+ */
321
+ interface FluxTraceEvent {
322
+ /** The type of event being recorded. */
323
+ type: FluxTraceEventType;
324
+ /** Unix timestamp in milliseconds. */
325
+ timestamp: number;
326
+ /** ID of the workflow instance. */
327
+ workflowId: string;
328
+ /** Name of the workflow definition. */
329
+ workflowName: string;
330
+ /** Name of the step, if applicable. */
331
+ stepName?: string;
332
+ /** Index of the step, if applicable. */
333
+ stepIndex?: number;
334
+ /** Whether the step was a commit. */
335
+ commit?: boolean;
336
+ /** Current retry attempt number. */
337
+ retries?: number;
338
+ /** Maximum allowed retries. */
339
+ maxRetries?: number;
340
+ /** Duration of the operation in milliseconds. */
341
+ duration?: number;
342
+ /** Error message if the event represents a failure. */
343
+ error?: string;
344
+ /** Current status of the workflow or step. */
345
+ status?: WorkflowStatus | StepExecution['status'];
346
+ /** Input data associated with the event. */
347
+ input?: unknown;
348
+ /** Current workflow data associated with the event. */
349
+ data?: Record<string, unknown>;
350
+ /** Additional contextual metadata. */
351
+ meta?: Record<string, unknown>;
352
+ }
353
+ /**
354
+ * Destination for trace events, such as OpenTelemetry, ELK, or a simple console.
355
+ */
356
+ interface FluxTraceSink {
357
+ /**
358
+ * Processes a trace event.
359
+ * @param event - The event to record.
360
+ */
361
+ emit(event: FluxTraceEvent): void | Promise<void>;
362
+ }
363
+ /**
364
+ * Global configuration for the Flux engine instance.
365
+ */
366
+ interface FluxConfig<TData = Record<string, any>> {
367
+ /** Persistence provider for workflow states. */
368
+ storage?: WorkflowStorage;
369
+ /** Logger for internal engine operations. */
370
+ logger?: FluxLogger;
371
+ /** Telemetry sink for execution events. */
372
+ trace?: FluxTraceSink;
373
+ /** Default retry limit for steps that don't specify one. */
374
+ defaultRetries?: number;
375
+ /** Default timeout in milliseconds for steps that don't specify one. */
376
+ defaultTimeout?: number;
377
+ /** Whether to allow parallel execution of independent workflows (if supported). */
378
+ parallel?: boolean;
379
+ /** Lifecycle hooks for custom logic at specific execution points. */
380
+ on?: {
381
+ stepStart?: <TInput = unknown>(step: string, ctx: WorkflowContext<TInput, TData>) => void;
382
+ stepComplete?: <TInput = unknown>(step: string, ctx: WorkflowContext<TInput, TData>, result: StepResult) => void;
383
+ stepError?: <TInput = unknown>(step: string, ctx: WorkflowContext<TInput, TData>, error: Error) => void;
384
+ workflowComplete?: <TInput = unknown>(ctx: WorkflowContext<TInput, TData>) => void;
385
+ workflowError?: <TInput = unknown>(ctx: WorkflowContext<TInput, TData>, error: Error) => void;
386
+ };
387
+ /** Configuration for data optimization (handling large objects). */
388
+ optimizer?: {
389
+ enabled: boolean;
390
+ threshold?: number;
391
+ defaultLocation?: 's3' | 'redis' | 'database' | 'memory';
392
+ };
393
+ /** Distributed lock provider for cluster mode. */
394
+ lockProvider?: LockProvider;
395
+ }
396
+ /**
397
+ * Configuration for a scheduled workflow execution.
398
+ */
399
+ interface CronScheduleOptions<TInput = unknown> {
400
+ /** Unique identifier for the schedule. */
401
+ id: string;
402
+ /** Cron expression (e.g., "* * * * *"). */
403
+ cron: string;
404
+ /** Workflow definition to execute. */
405
+ workflow: WorkflowDefinition<TInput, any> | string;
406
+ /** Input to provide to the workflow. */
407
+ input: TInput;
408
+ /** Optional metadata for the schedule. */
409
+ meta?: Record<string, unknown>;
410
+ /** Whether the schedule is active. @default true */
411
+ enabled?: boolean;
412
+ }
413
+ /**
414
+ * The final outcome of a workflow execution.
415
+ */
416
+ interface FluxResult<TData = Record<string, any>> {
417
+ /** Unique ID of the workflow instance. */
418
+ id: string;
419
+ /** Final lifecycle status. */
420
+ status: WorkflowStatus;
421
+ /** Final state of the workflow data. */
422
+ data: TData;
423
+ /** Full execution history. */
424
+ history: StepExecution[];
425
+ /** Total execution time in milliseconds. */
426
+ duration: number;
427
+ /** The error that caused failure, if any. */
428
+ error?: Error;
429
+ /** Final version of the workflow state. */
430
+ version: number;
431
+ }
432
+
433
+ export { type CronScheduleOptions as C, type FluxWaitResult as F, type Lock as L, MemoryLockProvider as M, type StepDefinition as S, type WorkflowDefinition as W, type WorkflowState as a, type FluxConfig as b, type FluxLogger as c, type FluxResult as d, type FluxTraceEvent as e, type FluxTraceEventType as f, type FluxTraceSink as g, type LockProvider as h, type StepDescriptor as i, type StepExecution as j, type StepResult as k, type WorkflowContext as l, type WorkflowDescriptor as m, type WorkflowFilter as n, type WorkflowStatus as o, type WorkflowStorage as p, type StepHandlerResult as q };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravito/flux",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Platform-agnostic workflow engine for Gravito",
5
5
  "type": "module",
6
6
  "main": "./dist/index.node.cjs",
@@ -41,21 +41,29 @@
41
41
  "build": "tsup",
42
42
  "dev:viewer": "node ./bin/flux.js dev --trace ./.flux/trace.ndjson --port 4280",
43
43
  "typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
44
- "test": "bun test",
45
- "test:coverage": "bun test --coverage --coverage-threshold=80",
46
- "test:ci": "bun test --coverage --coverage-threshold=80"
44
+ "test": "bun test --timeout=10000",
45
+ "test:coverage": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
46
+ "test:ci": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
47
+ "test:unit": "bun test tests/ --timeout=10000",
48
+ "test:integration": "test $(find tests -name '*.integration.test.ts' 2>/dev/null | wc -l) -gt 0 && find tests -name '*.integration.test.ts' -print0 | xargs -0 bun test --timeout=10000 || echo 'No integration tests found'"
47
49
  },
48
50
  "peerDependencies": {
49
- "@gravito/core": "workspace:*"
51
+ "@gravito/core": "^1.6.1",
52
+ "pg": "^8.18.0"
50
53
  },
51
54
  "peerDependenciesMeta": {
52
55
  "@gravito/core": {
53
56
  "optional": true
57
+ },
58
+ "pg": {
59
+ "optional": true
54
60
  }
55
61
  },
56
62
  "devDependencies": {
63
+ "@gravito/core": "^1.6.1",
64
+ "@types/pg": "^8.16.0",
57
65
  "bun-types": "latest",
58
- "@gravito/core": "workspace:*",
66
+ "pg": "^8.18.0",
59
67
  "tsup": "^8.5.1",
60
68
  "typescript": "^5.9.3"
61
69
  },
@@ -76,5 +84,8 @@
76
84
  },
77
85
  "publishConfig": {
78
86
  "access": "public"
87
+ },
88
+ "dependencies": {
89
+ "cron-parser": "^5.5.0"
79
90
  }
80
91
  }