@gravito/flux 3.0.1 → 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.
- package/README.md +298 -0
- package/bin/flux.js +25 -1
- package/dev/viewer/app.js +4 -4
- package/dist/bun.cjs +2 -2
- package/dist/bun.cjs.map +1 -1
- package/dist/bun.d.cts +65 -26
- package/dist/bun.d.ts +65 -26
- package/dist/bun.js +1 -1
- package/dist/chunk-4DXCQ6CL.js +3486 -0
- package/dist/chunk-4DXCQ6CL.js.map +1 -0
- package/dist/chunk-6AZNHVEO.cjs +316 -0
- package/dist/chunk-6AZNHVEO.cjs.map +1 -0
- package/dist/{chunk-ZAMVC732.js → chunk-NAIVO7RR.js} +64 -15
- package/dist/chunk-NAIVO7RR.js.map +1 -0
- package/dist/chunk-WAPZDXSX.cjs +3486 -0
- package/dist/chunk-WAPZDXSX.cjs.map +1 -0
- package/dist/chunk-WGDTB6OC.js +316 -0
- package/dist/chunk-WGDTB6OC.js.map +1 -0
- package/dist/{chunk-SJSPR4ZU.cjs → chunk-YXBEYVGY.cjs} +66 -17
- package/dist/chunk-YXBEYVGY.cjs.map +1 -0
- package/dist/cli/flux-visualize.cjs +108 -0
- package/dist/cli/flux-visualize.cjs.map +1 -0
- package/dist/cli/flux-visualize.d.cts +1 -0
- package/dist/cli/flux-visualize.d.ts +1 -0
- package/dist/cli/flux-visualize.js +108 -0
- package/dist/cli/flux-visualize.js.map +1 -0
- package/dist/index.cjs +97 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +369 -13
- package/dist/index.d.ts +369 -13
- package/dist/index.js +96 -8
- package/dist/index.js.map +1 -1
- package/dist/index.node.cjs +11 -3
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.cts +1110 -247
- package/dist/index.node.d.ts +1110 -247
- package/dist/index.node.js +10 -2
- package/dist/types-CRz5XdLd.d.cts +433 -0
- package/dist/types-CRz5XdLd.d.ts +433 -0
- package/package.json +17 -6
- package/dist/chunk-3JGQYHUN.js +0 -1006
- package/dist/chunk-3JGQYHUN.js.map +0 -1
- package/dist/chunk-5OXXH442.cjs +0 -1006
- package/dist/chunk-5OXXH442.cjs.map +0 -1
- package/dist/chunk-SJSPR4ZU.cjs.map +0 -1
- package/dist/chunk-ZAMVC732.js.map +0 -1
- package/dist/types-CZwYGpou.d.cts +0 -353
- package/dist/types-CZwYGpou.d.ts +0 -353
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,255 @@
|
|
|
1
|
-
import { W as WorkflowDefinition, F as FluxWaitResult } from './types-
|
|
2
|
-
export {
|
|
1
|
+
import { C as CronScheduleOptions, W as WorkflowDefinition, a as WorkflowState, F as FluxWaitResult } from './types-CRz5XdLd.js';
|
|
2
|
+
export { b as FluxConfig, c as FluxLogger, d as FluxResult, e as FluxTraceEvent, f as FluxTraceEventType, g as FluxTraceSink, L as Lock, h as LockProvider, M as MemoryLockProvider, S as StepDefinition, i as StepDescriptor, j as StepExecution, k as StepResult, l as WorkflowContext, m as WorkflowDescriptor, n as WorkflowFilter, o as WorkflowStatus, p as WorkflowStorage } from './types-CRz5XdLd.js';
|
|
3
3
|
import { FluxEngine } from './index.node.js';
|
|
4
|
-
export { ContextManager, FluxConsoleLogger, FluxSilentLogger, JsonFileTraceSink, MemoryStorage, OrbitFlux, OrbitFluxOptions, StateMachine, StepExecutor, WorkflowBuilder, createWorkflow } from './index.node.js';
|
|
4
|
+
export { BatchExecutionOptions, BatchExecutor, BatchItemResult, BatchResult, ContextManager, FluxConsoleLogger, FluxSilentLogger, JsonFileTraceSink, MemoryStorage, OrbitFlux, OrbitFluxOptions, PostgreSQLStorage, PostgreSQLStorageOptions, RedisClient, RedisLockProvider, RedisLockProviderOptions, StateMachine, StepExecutor, WorkflowBuilder, createWorkflow } from './index.node.js';
|
|
5
5
|
export { BunSQLiteStorage, BunSQLiteStorageOptions } from './bun.js';
|
|
6
6
|
import '@gravito/core';
|
|
7
7
|
import 'bun:sqlite';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Standard error codes for FluxEngine operations.
|
|
11
|
+
*
|
|
12
|
+
* Used to programmatically identify the cause of a `FluxError`.
|
|
13
|
+
*/
|
|
14
|
+
declare enum FluxErrorCode {
|
|
15
|
+
/** The requested workflow instance could not be found in storage. */
|
|
16
|
+
WORKFLOW_NOT_FOUND = "WORKFLOW_NOT_FOUND",
|
|
17
|
+
/** The input data provided to the workflow failed validation. */
|
|
18
|
+
WORKFLOW_INVALID_INPUT = "WORKFLOW_INVALID_INPUT",
|
|
19
|
+
/** The workflow definition has changed since the instance was created, making it unsafe to resume. */
|
|
20
|
+
WORKFLOW_DEFINITION_CHANGED = "WORKFLOW_DEFINITION_CHANGED",
|
|
21
|
+
/** The workflow name in the definition does not match the stored state. */
|
|
22
|
+
WORKFLOW_NAME_MISMATCH = "WORKFLOW_NAME_MISMATCH",
|
|
23
|
+
/** An attempt was made to transition the workflow to an incompatible state. */
|
|
24
|
+
INVALID_STATE_TRANSITION = "INVALID_STATE_TRANSITION",
|
|
25
|
+
/** An operation requiring a suspended state was attempted on a workflow that is not suspended. */
|
|
26
|
+
WORKFLOW_NOT_SUSPENDED = "WORKFLOW_NOT_SUSPENDED",
|
|
27
|
+
/** The requested step index is out of bounds for the current workflow definition. */
|
|
28
|
+
INVALID_STEP_INDEX = "INVALID_STEP_INDEX",
|
|
29
|
+
/** A workflow step exceeded its configured execution time limit. */
|
|
30
|
+
STEP_TIMEOUT = "STEP_TIMEOUT",
|
|
31
|
+
/** The requested step could not be found in the workflow definition. */
|
|
32
|
+
STEP_NOT_FOUND = "STEP_NOT_FOUND",
|
|
33
|
+
/** Multiple concurrent attempts to modify the same workflow instance were detected. */
|
|
34
|
+
CONCURRENT_MODIFICATION = "CONCURRENT_MODIFICATION",
|
|
35
|
+
/** A workflow definition must contain at least one step to be executable. */
|
|
36
|
+
EMPTY_WORKFLOW = "EMPTY_WORKFLOW",
|
|
37
|
+
/** No recovery action is registered for a step that requires recovery. */
|
|
38
|
+
NO_RECOVERY_ACTION = "NO_RECOVERY_ACTION",
|
|
39
|
+
/** An invalid JSON Pointer was provided for state manipulation. */
|
|
40
|
+
INVALID_JSON_POINTER = "INVALID_JSON_POINTER",
|
|
41
|
+
/** Cannot access a property on a non-object value in the state tree. */
|
|
42
|
+
INVALID_PATH_TRAVERSAL = "INVALID_PATH_TRAVERSAL",
|
|
43
|
+
/** Cannot replace the root object of the workflow state. */
|
|
44
|
+
CANNOT_REPLACE_ROOT = "CANNOT_REPLACE_ROOT",
|
|
45
|
+
/** Cannot remove the root object of the workflow state. */
|
|
46
|
+
CANNOT_REMOVE_ROOT = "CANNOT_REMOVE_ROOT"
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Base class for all errors thrown by the FluxEngine.
|
|
50
|
+
*
|
|
51
|
+
* Includes a machine-readable error code and optional context for debugging.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* try {
|
|
56
|
+
* await engine.execute(flow, input);
|
|
57
|
+
* } catch (err) {
|
|
58
|
+
* if (err instanceof FluxError && err.code === FluxErrorCode.STEP_TIMEOUT) {
|
|
59
|
+
* console.error('Workflow timed out');
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
declare class FluxError extends Error {
|
|
65
|
+
readonly code: FluxErrorCode;
|
|
66
|
+
readonly context?: Record<string, unknown> | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new FluxError.
|
|
69
|
+
*
|
|
70
|
+
* @param message - Human-readable error description.
|
|
71
|
+
* @param code - Machine-readable error code.
|
|
72
|
+
* @param context - Additional metadata related to the error.
|
|
73
|
+
*/
|
|
74
|
+
constructor(message: string, code: FluxErrorCode, context?: Record<string, unknown> | undefined);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates a FluxError for a missing workflow instance.
|
|
78
|
+
*
|
|
79
|
+
* @param id - The unique identifier of the missing workflow.
|
|
80
|
+
* @returns A FluxError with the WORKFLOW_NOT_FOUND code.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* throw workflowNotFound('wf-123');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
declare function workflowNotFound(id: string): FluxError;
|
|
88
|
+
/**
|
|
89
|
+
* Creates a FluxError for an illegal state transition.
|
|
90
|
+
*
|
|
91
|
+
* @param from - The current state of the workflow.
|
|
92
|
+
* @param to - The attempted target state.
|
|
93
|
+
* @returns A FluxError with the INVALID_STATE_TRANSITION code.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* throw invalidStateTransition('completed', 'running');
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function invalidStateTransition(from: string, to: string): FluxError;
|
|
101
|
+
/**
|
|
102
|
+
* Creates a FluxError for invalid workflow input.
|
|
103
|
+
*
|
|
104
|
+
* @param workflowName - The name of the workflow definition.
|
|
105
|
+
* @returns A FluxError with the WORKFLOW_INVALID_INPUT code.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* throw invalidInput('order-process');
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare function invalidInput(workflowName: string): FluxError;
|
|
113
|
+
/**
|
|
114
|
+
* Creates a FluxError for a workflow name mismatch.
|
|
115
|
+
*
|
|
116
|
+
* @param expected - The name expected by the definition.
|
|
117
|
+
* @param received - The name found in the stored state.
|
|
118
|
+
* @returns A FluxError with the WORKFLOW_NAME_MISMATCH code.
|
|
119
|
+
*/
|
|
120
|
+
declare function workflowNameMismatch(expected: string, received: string): FluxError;
|
|
121
|
+
/**
|
|
122
|
+
* Creates a FluxError when a workflow definition has changed incompatibly.
|
|
123
|
+
*
|
|
124
|
+
* @returns A FluxError with the WORKFLOW_DEFINITION_CHANGED code.
|
|
125
|
+
*/
|
|
126
|
+
declare function workflowDefinitionChanged(): FluxError;
|
|
127
|
+
/**
|
|
128
|
+
* Creates a FluxError when an operation requires a suspended workflow.
|
|
129
|
+
*
|
|
130
|
+
* @param status - The current status of the workflow.
|
|
131
|
+
* @returns A FluxError with the WORKFLOW_NOT_SUSPENDED code.
|
|
132
|
+
*/
|
|
133
|
+
declare function workflowNotSuspended(status: string): FluxError;
|
|
134
|
+
/**
|
|
135
|
+
* Creates a FluxError when a specific step cannot be found.
|
|
136
|
+
*
|
|
137
|
+
* @param step - The name or index of the missing step.
|
|
138
|
+
* @returns A FluxError with the STEP_NOT_FOUND code.
|
|
139
|
+
*/
|
|
140
|
+
declare function stepNotFound(step: string | number): FluxError;
|
|
141
|
+
/**
|
|
142
|
+
* Creates a FluxError for an out-of-bounds step index.
|
|
143
|
+
*
|
|
144
|
+
* @param index - The invalid step index.
|
|
145
|
+
* @returns A FluxError with the INVALID_STEP_INDEX code.
|
|
146
|
+
*/
|
|
147
|
+
declare function invalidStepIndex(index: number): FluxError;
|
|
148
|
+
/**
|
|
149
|
+
* Creates a FluxError for an empty workflow (no steps defined).
|
|
150
|
+
*
|
|
151
|
+
* @param workflowName - The name of the workflow.
|
|
152
|
+
* @returns A FluxError with the EMPTY_WORKFLOW code.
|
|
153
|
+
*/
|
|
154
|
+
declare function emptyWorkflow(workflowName: string): FluxError;
|
|
155
|
+
/**
|
|
156
|
+
* Creates a FluxError when no recovery action is registered for a step.
|
|
157
|
+
*
|
|
158
|
+
* @param stepName - The name of the step requiring recovery.
|
|
159
|
+
* @returns A FluxError with the NO_RECOVERY_ACTION code.
|
|
160
|
+
*/
|
|
161
|
+
declare function noRecoveryAction(stepName: string): FluxError;
|
|
162
|
+
/**
|
|
163
|
+
* Creates a FluxError for invalid JSON Pointer syntax.
|
|
164
|
+
*
|
|
165
|
+
* @param path - The invalid JSON Pointer.
|
|
166
|
+
* @returns A FluxError with the INVALID_JSON_POINTER code.
|
|
167
|
+
*/
|
|
168
|
+
declare function invalidJsonPointer(path: string): FluxError;
|
|
169
|
+
/**
|
|
170
|
+
* Creates a FluxError when attempting to traverse a non-object value.
|
|
171
|
+
*
|
|
172
|
+
* @param segment - The property being accessed.
|
|
173
|
+
* @param current - The current value type.
|
|
174
|
+
* @returns A FluxError with the INVALID_PATH_TRAVERSAL code.
|
|
175
|
+
*/
|
|
176
|
+
declare function invalidPathTraversal(segment: string, current: unknown): FluxError;
|
|
177
|
+
/**
|
|
178
|
+
* Creates a FluxError when attempting to replace the root object.
|
|
179
|
+
*
|
|
180
|
+
* @returns A FluxError with the CANNOT_REPLACE_ROOT code.
|
|
181
|
+
*/
|
|
182
|
+
declare function cannotReplaceRoot(): FluxError;
|
|
183
|
+
/**
|
|
184
|
+
* Creates a FluxError when attempting to remove the root object.
|
|
185
|
+
*
|
|
186
|
+
* @returns A FluxError with the CANNOT_REMOVE_ROOT code.
|
|
187
|
+
*/
|
|
188
|
+
declare function cannotRemoveRoot(): FluxError;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Built-in scheduler for Flux workflows.
|
|
192
|
+
*
|
|
193
|
+
* Manages multiple cron-based schedules and triggers workflow execution
|
|
194
|
+
* at the specified intervals.
|
|
195
|
+
*/
|
|
196
|
+
declare class CronTrigger {
|
|
197
|
+
private engine;
|
|
198
|
+
private schedules;
|
|
199
|
+
private timers;
|
|
200
|
+
private running;
|
|
201
|
+
/**
|
|
202
|
+
* Creates a new CronTrigger instance.
|
|
203
|
+
*
|
|
204
|
+
* @param engine - The Flux engine to use for executing scheduled workflows.
|
|
205
|
+
*/
|
|
206
|
+
constructor(engine: FluxEngine);
|
|
207
|
+
/**
|
|
208
|
+
* Starts the scheduler.
|
|
209
|
+
*/
|
|
210
|
+
start(): void;
|
|
211
|
+
/**
|
|
212
|
+
* Stops the scheduler and clears all timers.
|
|
213
|
+
*/
|
|
214
|
+
stop(): void;
|
|
215
|
+
/**
|
|
216
|
+
* Adds a new schedule or updates an existing one.
|
|
217
|
+
*
|
|
218
|
+
* @param options - The schedule configuration.
|
|
219
|
+
*/
|
|
220
|
+
addSchedule(options: CronScheduleOptions): void;
|
|
221
|
+
/**
|
|
222
|
+
* Removes a schedule.
|
|
223
|
+
*
|
|
224
|
+
* @param id - The ID of the schedule to remove.
|
|
225
|
+
*/
|
|
226
|
+
removeSchedule(id: string): void;
|
|
227
|
+
/**
|
|
228
|
+
* Refreshes all active schedules.
|
|
229
|
+
* @private
|
|
230
|
+
*/
|
|
231
|
+
private refreshAll;
|
|
232
|
+
/**
|
|
233
|
+
* Calculates the next execution time and sets a timer for a specific schedule.
|
|
234
|
+
*
|
|
235
|
+
* @param id - The ID of the schedule to refresh.
|
|
236
|
+
* @private
|
|
237
|
+
*/
|
|
238
|
+
private refreshSchedule;
|
|
239
|
+
/**
|
|
240
|
+
* Lists all registered schedules.
|
|
241
|
+
* @returns An array of schedule configurations.
|
|
242
|
+
*/
|
|
243
|
+
listSchedules(): CronScheduleOptions[];
|
|
244
|
+
}
|
|
245
|
+
|
|
9
246
|
/**
|
|
10
247
|
* Performance metrics captured during a workflow profiling session.
|
|
11
248
|
*
|
|
249
|
+
* Used to quantify the resource footprint of a workflow execution, enabling
|
|
250
|
+
* data-driven decisions for infrastructure scaling and concurrency tuning.
|
|
251
|
+
*
|
|
12
252
|
* @public
|
|
13
|
-
* @since 3.0.0
|
|
14
253
|
*/
|
|
15
254
|
interface ProfileMetrics {
|
|
16
255
|
/** Total wall-clock duration of the workflow execution in milliseconds. */
|
|
@@ -27,8 +266,10 @@ interface ProfileMetrics {
|
|
|
27
266
|
/**
|
|
28
267
|
* Concurrency recommendations generated by the profiler.
|
|
29
268
|
*
|
|
269
|
+
* Provides actionable insights into how many instances of a workflow can safely
|
|
270
|
+
* and efficiently run in parallel on the current hardware.
|
|
271
|
+
*
|
|
30
272
|
* @public
|
|
31
|
-
* @since 3.0.0
|
|
32
273
|
*/
|
|
33
274
|
interface ProfileRecommendation {
|
|
34
275
|
/** The identified primary bottleneck type. */
|
|
@@ -53,21 +294,51 @@ interface ProfileRecommendation {
|
|
|
53
294
|
* const profiler = new WorkflowProfiler();
|
|
54
295
|
* const metrics = await profiler.profile(myWorkflow, { input: 'data' });
|
|
55
296
|
* const advice = profiler.recommend(metrics);
|
|
56
|
-
* console.log(advice.suggestedConcurrency);
|
|
297
|
+
* console.log(`Suggested concurrency: ${advice.suggestedConcurrency}`);
|
|
57
298
|
* ```
|
|
58
299
|
*
|
|
59
300
|
* @public
|
|
60
|
-
* @since 3.0.0
|
|
61
301
|
*/
|
|
62
302
|
declare class WorkflowProfiler {
|
|
63
303
|
private engine?;
|
|
304
|
+
/**
|
|
305
|
+
* Initializes the profiler with an optional engine.
|
|
306
|
+
*
|
|
307
|
+
* @param engine - The FluxEngine instance to use for execution. If omitted, a silent engine is created.
|
|
308
|
+
*/
|
|
64
309
|
constructor(engine?: FluxEngine | undefined);
|
|
65
310
|
/**
|
|
66
|
-
*
|
|
311
|
+
* Executes a workflow and captures its resource consumption metrics.
|
|
312
|
+
*
|
|
313
|
+
* Performs a warmup run to ensure JIT optimization before measurement.
|
|
314
|
+
*
|
|
315
|
+
* @param workflow - The workflow definition to profile.
|
|
316
|
+
* @param input - The input data for the workflow execution.
|
|
317
|
+
* @returns A promise resolving to the captured performance metrics.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* const metrics = await profiler.profile(orderWorkflow, { id: '123' });
|
|
322
|
+
* console.log(`Duration: ${metrics.durationMs}ms`);
|
|
323
|
+
* ```
|
|
67
324
|
*/
|
|
68
325
|
profile<TInput>(workflow: WorkflowDefinition<TInput, any>, input: TInput): Promise<ProfileMetrics>;
|
|
69
326
|
/**
|
|
70
|
-
*
|
|
327
|
+
* Analyzes metrics to generate concurrency recommendations.
|
|
328
|
+
*
|
|
329
|
+
* Considers system CPU cores and total memory to calculate safe and efficient limits.
|
|
330
|
+
*
|
|
331
|
+
* @param metrics - The metrics captured during a profiling session.
|
|
332
|
+
* @param config - Optional current configuration to check against recommendations.
|
|
333
|
+
* @returns A recommendation object containing bottleneck analysis and concurrency limits.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const advice = profiler.recommend(metrics, { configuredConcurrency: 10 });
|
|
338
|
+
* if (advice.type === 'CPU_BOUND') {
|
|
339
|
+
* console.warn('Workflow is CPU bound, consider reducing concurrency');
|
|
340
|
+
* }
|
|
341
|
+
* ```
|
|
71
342
|
*/
|
|
72
343
|
recommend(metrics: ProfileMetrics, config?: {
|
|
73
344
|
configuredConcurrency?: number;
|
|
@@ -75,15 +346,100 @@ declare class WorkflowProfiler {
|
|
|
75
346
|
}
|
|
76
347
|
|
|
77
348
|
/**
|
|
78
|
-
*
|
|
349
|
+
* Options for customizing Mermaid diagram generation.
|
|
350
|
+
*/
|
|
351
|
+
interface MermaidOptions {
|
|
352
|
+
/** Include detailed step information (retries, timeout, conditions). */
|
|
353
|
+
showDetails?: boolean;
|
|
354
|
+
/** Include execution history with status colors. */
|
|
355
|
+
showStatus?: boolean;
|
|
356
|
+
/** Render parallel groups visually. */
|
|
357
|
+
showParallelGroups?: boolean;
|
|
358
|
+
/** Theme: 'default' | 'dark' | 'forest' | 'neutral'. */
|
|
359
|
+
theme?: 'default' | 'dark' | 'forest' | 'neutral';
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Generates Mermaid flowchart diagrams from workflow definitions and execution states.
|
|
363
|
+
*
|
|
364
|
+
* **Features**:
|
|
365
|
+
* - Workflow structure visualization (steps, parallel groups, conditions)
|
|
366
|
+
* - Execution status overlay (completed, failed, compensated)
|
|
367
|
+
* - Detailed metadata (retries, timeout, commit markers)
|
|
368
|
+
*
|
|
369
|
+
* **Use Cases**:
|
|
370
|
+
* - Documentation generation
|
|
371
|
+
* - Debugging workflow execution
|
|
372
|
+
* - Visual workflow design validation
|
|
373
|
+
*/
|
|
374
|
+
declare class MermaidGenerator {
|
|
375
|
+
/**
|
|
376
|
+
* Generates a Mermaid flowchart from a workflow definition.
|
|
377
|
+
*
|
|
378
|
+
* @param definition - The workflow definition to visualize
|
|
379
|
+
* @param options - Customization options
|
|
380
|
+
* @returns Mermaid diagram syntax as a string
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* const generator = new MermaidGenerator()
|
|
385
|
+
* const diagram = generator.generateFromDefinition(workflow, {
|
|
386
|
+
* showDetails: true,
|
|
387
|
+
* showParallelGroups: true
|
|
388
|
+
* })
|
|
389
|
+
* console.log(diagram)
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
generateFromDefinition<TInput, TData>(definition: WorkflowDefinition<TInput, TData>, options?: MermaidOptions): string;
|
|
393
|
+
/**
|
|
394
|
+
* Generates a Mermaid flowchart from a workflow execution state.
|
|
395
|
+
* Overlays execution status (completed, failed, compensated) on top of the structure.
|
|
396
|
+
*
|
|
397
|
+
* @param definition - The workflow definition
|
|
398
|
+
* @param state - The current execution state
|
|
399
|
+
* @param options - Customization options
|
|
400
|
+
* @returns Mermaid diagram syntax with status overlay
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```typescript
|
|
404
|
+
* const diagram = generator.generateFromState(workflow, executionState, {
|
|
405
|
+
* showStatus: true,
|
|
406
|
+
* showDetails: true
|
|
407
|
+
* })
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
generateFromState<TInput, TData>(definition: WorkflowDefinition<TInput, TData>, state: WorkflowState<TInput, TData>, options?: MermaidOptions): string;
|
|
411
|
+
private sanitizeNodeId;
|
|
412
|
+
private buildStepLabel;
|
|
413
|
+
private buildStepLabelWithStatus;
|
|
414
|
+
private getStatusClass;
|
|
415
|
+
private getWorkflowFinalStatus;
|
|
416
|
+
private getNextNodeId;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Flux helper utilities for workflow control flow.
|
|
421
|
+
*
|
|
422
|
+
* Provides methods to interact with the workflow engine's special behaviors,
|
|
423
|
+
* such as suspending execution to wait for external signals.
|
|
79
424
|
*/
|
|
80
425
|
declare const Flux: {
|
|
81
426
|
/**
|
|
82
|
-
*
|
|
427
|
+
* Suspends workflow execution until a specific signal is received.
|
|
428
|
+
*
|
|
429
|
+
* When a handler returns this result, the engine saves the current state
|
|
430
|
+
* and stops execution. The workflow can be resumed later using `engine.signal()`.
|
|
431
|
+
*
|
|
432
|
+
* @param signal - The unique identifier for the signal to wait for.
|
|
433
|
+
* @returns A special result object that instructs the engine to suspend.
|
|
83
434
|
*
|
|
84
|
-
* @
|
|
435
|
+
* @example
|
|
436
|
+
* ```typescript
|
|
437
|
+
* .step('wait-for-approval', async (ctx) => {
|
|
438
|
+
* return Flux.wait('manager-approval');
|
|
439
|
+
* })
|
|
440
|
+
* ```
|
|
85
441
|
*/
|
|
86
442
|
wait: (signal: string) => FluxWaitResult;
|
|
87
443
|
};
|
|
88
444
|
|
|
89
|
-
export { Flux, FluxEngine, FluxWaitResult, type ProfileMetrics, type ProfileRecommendation, WorkflowDefinition, WorkflowProfiler };
|
|
445
|
+
export { CronScheduleOptions, CronTrigger, Flux, FluxEngine, FluxError, FluxErrorCode, FluxWaitResult, MermaidGenerator, type MermaidOptions, type ProfileMetrics, type ProfileRecommendation, WorkflowDefinition, WorkflowProfiler, WorkflowState, cannotRemoveRoot, cannotReplaceRoot, emptyWorkflow, invalidInput, invalidJsonPointer, invalidPathTraversal, invalidStateTransition, invalidStepIndex, noRecoveryAction, stepNotFound, workflowDefinitionChanged, workflowNameMismatch, workflowNotFound, workflowNotSuspended };
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,52 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BatchExecutor,
|
|
2
3
|
ContextManager,
|
|
4
|
+
CronTrigger,
|
|
3
5
|
FluxConsoleLogger,
|
|
4
6
|
FluxEngine,
|
|
7
|
+
FluxError,
|
|
8
|
+
FluxErrorCode,
|
|
5
9
|
FluxSilentLogger,
|
|
6
10
|
JsonFileTraceSink,
|
|
11
|
+
MemoryLockProvider,
|
|
7
12
|
MemoryStorage,
|
|
8
13
|
OrbitFlux,
|
|
14
|
+
PostgreSQLStorage,
|
|
15
|
+
RedisLockProvider,
|
|
9
16
|
StateMachine,
|
|
10
17
|
StepExecutor,
|
|
11
18
|
WorkflowBuilder,
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
cannotRemoveRoot,
|
|
20
|
+
cannotReplaceRoot,
|
|
21
|
+
createWorkflow,
|
|
22
|
+
emptyWorkflow,
|
|
23
|
+
invalidInput,
|
|
24
|
+
invalidJsonPointer,
|
|
25
|
+
invalidPathTraversal,
|
|
26
|
+
invalidStateTransition,
|
|
27
|
+
invalidStepIndex,
|
|
28
|
+
noRecoveryAction,
|
|
29
|
+
stepNotFound,
|
|
30
|
+
workflowDefinitionChanged,
|
|
31
|
+
workflowNameMismatch,
|
|
32
|
+
workflowNotFound,
|
|
33
|
+
workflowNotSuspended
|
|
34
|
+
} from "./chunk-4DXCQ6CL.js";
|
|
14
35
|
import {
|
|
15
36
|
BunSQLiteStorage
|
|
16
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-NAIVO7RR.js";
|
|
38
|
+
import {
|
|
39
|
+
MermaidGenerator
|
|
40
|
+
} from "./chunk-WGDTB6OC.js";
|
|
17
41
|
|
|
18
42
|
// src/profiler/WorkflowProfiler.ts
|
|
19
43
|
import * as os from "os";
|
|
20
44
|
var WorkflowProfiler = class {
|
|
45
|
+
/**
|
|
46
|
+
* Initializes the profiler with an optional engine.
|
|
47
|
+
*
|
|
48
|
+
* @param engine - The FluxEngine instance to use for execution. If omitted, a silent engine is created.
|
|
49
|
+
*/
|
|
21
50
|
constructor(engine) {
|
|
22
51
|
this.engine = engine;
|
|
23
52
|
if (!this.engine) {
|
|
@@ -27,7 +56,19 @@ var WorkflowProfiler = class {
|
|
|
27
56
|
}
|
|
28
57
|
}
|
|
29
58
|
/**
|
|
30
|
-
*
|
|
59
|
+
* Executes a workflow and captures its resource consumption metrics.
|
|
60
|
+
*
|
|
61
|
+
* Performs a warmup run to ensure JIT optimization before measurement.
|
|
62
|
+
*
|
|
63
|
+
* @param workflow - The workflow definition to profile.
|
|
64
|
+
* @param input - The input data for the workflow execution.
|
|
65
|
+
* @returns A promise resolving to the captured performance metrics.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const metrics = await profiler.profile(orderWorkflow, { id: '123' });
|
|
70
|
+
* console.log(`Duration: ${metrics.durationMs}ms`);
|
|
71
|
+
* ```
|
|
31
72
|
*/
|
|
32
73
|
async profile(workflow, input) {
|
|
33
74
|
try {
|
|
@@ -60,7 +101,21 @@ var WorkflowProfiler = class {
|
|
|
60
101
|
};
|
|
61
102
|
}
|
|
62
103
|
/**
|
|
63
|
-
*
|
|
104
|
+
* Analyzes metrics to generate concurrency recommendations.
|
|
105
|
+
*
|
|
106
|
+
* Considers system CPU cores and total memory to calculate safe and efficient limits.
|
|
107
|
+
*
|
|
108
|
+
* @param metrics - The metrics captured during a profiling session.
|
|
109
|
+
* @param config - Optional current configuration to check against recommendations.
|
|
110
|
+
* @returns A recommendation object containing bottleneck analysis and concurrency limits.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const advice = profiler.recommend(metrics, { configuredConcurrency: 10 });
|
|
115
|
+
* if (advice.type === 'CPU_BOUND') {
|
|
116
|
+
* console.warn('Workflow is CPU bound, consider reducing concurrency');
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
64
119
|
*/
|
|
65
120
|
recommend(metrics, config) {
|
|
66
121
|
const totalMem = os.totalmem();
|
|
@@ -107,9 +162,20 @@ var WorkflowProfiler = class {
|
|
|
107
162
|
// src/index.ts
|
|
108
163
|
var Flux = {
|
|
109
164
|
/**
|
|
110
|
-
*
|
|
165
|
+
* Suspends workflow execution until a specific signal is received.
|
|
166
|
+
*
|
|
167
|
+
* When a handler returns this result, the engine saves the current state
|
|
168
|
+
* and stops execution. The workflow can be resumed later using `engine.signal()`.
|
|
169
|
+
*
|
|
170
|
+
* @param signal - The unique identifier for the signal to wait for.
|
|
171
|
+
* @returns A special result object that instructs the engine to suspend.
|
|
111
172
|
*
|
|
112
|
-
* @
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* .step('wait-for-approval', async (ctx) => {
|
|
176
|
+
* return Flux.wait('manager-approval');
|
|
177
|
+
* })
|
|
178
|
+
* ```
|
|
113
179
|
*/
|
|
114
180
|
wait: (signal) => ({
|
|
115
181
|
__kind: "flux_wait",
|
|
@@ -117,19 +183,41 @@ var Flux = {
|
|
|
117
183
|
})
|
|
118
184
|
};
|
|
119
185
|
export {
|
|
186
|
+
BatchExecutor,
|
|
120
187
|
BunSQLiteStorage,
|
|
121
188
|
ContextManager,
|
|
189
|
+
CronTrigger,
|
|
122
190
|
Flux,
|
|
123
191
|
FluxConsoleLogger,
|
|
124
192
|
FluxEngine,
|
|
193
|
+
FluxError,
|
|
194
|
+
FluxErrorCode,
|
|
125
195
|
FluxSilentLogger,
|
|
126
196
|
JsonFileTraceSink,
|
|
197
|
+
MemoryLockProvider,
|
|
127
198
|
MemoryStorage,
|
|
199
|
+
MermaidGenerator,
|
|
128
200
|
OrbitFlux,
|
|
201
|
+
PostgreSQLStorage,
|
|
202
|
+
RedisLockProvider,
|
|
129
203
|
StateMachine,
|
|
130
204
|
StepExecutor,
|
|
131
205
|
WorkflowBuilder,
|
|
132
206
|
WorkflowProfiler,
|
|
133
|
-
|
|
207
|
+
cannotRemoveRoot,
|
|
208
|
+
cannotReplaceRoot,
|
|
209
|
+
createWorkflow,
|
|
210
|
+
emptyWorkflow,
|
|
211
|
+
invalidInput,
|
|
212
|
+
invalidJsonPointer,
|
|
213
|
+
invalidPathTraversal,
|
|
214
|
+
invalidStateTransition,
|
|
215
|
+
invalidStepIndex,
|
|
216
|
+
noRecoveryAction,
|
|
217
|
+
stepNotFound,
|
|
218
|
+
workflowDefinitionChanged,
|
|
219
|
+
workflowNameMismatch,
|
|
220
|
+
workflowNotFound,
|
|
221
|
+
workflowNotSuspended
|
|
134
222
|
};
|
|
135
223
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/profiler/WorkflowProfiler.ts","../src/index.ts"],"sourcesContent":["import * as os from 'node:os'\nimport { FluxEngine } from '../engine/FluxEngine'\nimport { FluxSilentLogger } from '../logger/FluxLogger'\nimport type { WorkflowDefinition } from '../types'\n\n/**\n * Performance metrics captured during a workflow profiling session.\n *\n * @public\n * @since 3.0.0\n */\nexport interface ProfileMetrics {\n /** Total wall-clock duration of the workflow execution in milliseconds. */\n durationMs: number\n /** Amount of time spent in user-space CPU operations in milliseconds. */\n cpuUserMs: number\n /** Amount of time spent in system-space CPU operations in milliseconds. */\n cpuSysMs: number\n /** Estimated heap memory increase during execution in bytes. */\n memDeltaBytes: number\n /** Ratio of CPU time to wall-clock time (0.0 to 1.0+). Higher values indicate CPU-bound tasks. */\n cpuRatio: number\n}\n\n/**\n * Concurrency recommendations generated by the profiler.\n *\n * @public\n * @since 3.0.0\n */\nexport interface ProfileRecommendation {\n /** The identified primary bottleneck type. */\n type: 'IO_BOUND' | 'CPU_BOUND' | 'MEMORY_BOUND'\n /** Maximum safe concurrency considering memory and system stability. */\n safeConcurrency: number\n /** Most efficient concurrency level considering CPU utilization. */\n efficientConcurrency: number\n /** A suggested range for worker concurrency configuration. */\n suggestedConcurrency: string\n /** Detailed explanation for the recommendation. */\n reason: string\n}\n\n/**\n * WorkflowProfiler analyzes workflow performance characteristics.\n *\n * It measures CPU usage, memory consumption, and execution duration to recommend\n * optimal concurrency settings for Gravito Quasar workers or high-throughput consumers.\n *\n * @example\n * ```typescript\n * const profiler = new WorkflowProfiler();\n * const metrics = await profiler.profile(myWorkflow, { input: 'data' });\n * const advice = profiler.recommend(metrics);\n * console.log(advice.suggestedConcurrency);\n * ```\n *\n * @public\n * @since 3.0.0\n */\nexport class WorkflowProfiler {\n constructor(private engine?: FluxEngine) {\n if (!this.engine) {\n // Default minimal engine for profiling (Silent)\n this.engine = new FluxEngine({\n logger: new FluxSilentLogger(),\n })\n }\n }\n\n /**\n * Run a profile session for a specific workflow\n */\n async profile<TInput>(\n workflow: WorkflowDefinition<TInput, any>,\n input: TInput\n ): Promise<ProfileMetrics> {\n // 1. Warmup (JIT)\n try {\n await this.engine?.execute(workflow, input)\n } catch {}\n\n // 2. Measure\n if (global.gc) {\n global.gc()\n }\n\n const startCpu = process.cpuUsage()\n const startMem = process.memoryUsage().heapUsed\n const startTime = process.hrtime.bigint()\n\n await this.engine?.execute(workflow, input)\n\n const endTime = process.hrtime.bigint()\n const endCpu = process.cpuUsage(startCpu)\n const endMem = process.memoryUsage().heapUsed\n\n // 3. Calculate\n const durationNs = Number(endTime - startTime)\n const durationMs = durationNs / 1_000_000\n const cpuUserMs = endCpu.user / 1000\n const cpuSysMs = endCpu.system / 1000\n const totalCpuMs = cpuUserMs + cpuSysMs\n const memDeltaBytes = Math.max(0, endMem - startMem) // Clamp to 0\n\n // CPU Ratio: How much % of the time was spent on CPU vs Waiting\n const cpuRatio = totalCpuMs / durationMs\n\n return {\n durationMs,\n cpuUserMs,\n cpuSysMs,\n memDeltaBytes,\n cpuRatio,\n }\n }\n\n /**\n * Generate recommendations based on metrics and current environment\n */\n recommend(\n metrics: ProfileMetrics,\n config?: { configuredConcurrency?: number }\n ): ProfileRecommendation {\n const totalMem = os.totalmem()\n const cpus = os.cpus().length\n\n // 1. Analyze Bottleneck Type\n let type: ProfileRecommendation['type'] = 'IO_BOUND'\n if (metrics.cpuRatio > 0.5) {\n type = 'CPU_BOUND'\n } else if (metrics.memDeltaBytes > 50 * 1024 * 1024) {\n // > 50MB per run\n type = 'MEMORY_BOUND'\n }\n\n // 2. Calculate Limits\n\n // Memory Limit: Keep 30% buffer for system, divide rest by per-workflow memory\n const safeMem = totalMem * 0.7\n // Use at least 1MB as baseline to avoid division by zero or huge numbers\n const perInstanceMem = Math.max(metrics.memDeltaBytes, 1024 * 1024)\n const maxMemConcurrency = Math.floor(safeMem / perInstanceMem)\n\n // CPU Limit:\n // If IO Bound (0.2% cpu), we can run many. 100% / 0.2% = 500 tasks per core.\n // We cap efficiency at a reasonable number to avoid Event Loop Lag density.\n const cpuEfficiencyFactor = 1 / Math.max(metrics.cpuRatio, 0.001) // Avoid div by 0\n const maxCpuConcurrency = Math.floor(cpus * cpuEfficiencyFactor)\n\n // 3. Synthesize Recommendation\n const safe = Math.min(maxMemConcurrency, 200) // Hard cap at 200 for sanity\n let efficient = Math.min(maxCpuConcurrency, 200)\n\n // If CPU bound, strict limit based on cores\n if (type === 'CPU_BOUND') {\n efficient = cpus // 1:1 mapping is best for CPU bound\n }\n\n const recommended = Math.min(safe, efficient)\n\n let reason = ''\n if (type === 'IO_BOUND') {\n reason = `Workflow is I/O intensive (CPU usage ${(metrics.cpuRatio * 100).toFixed(1)}%). It is safe to run high concurrency up to ${recommended}.`\n } else if (type === 'CPU_BOUND') {\n reason = `Workflow is CPU intensive. Limiting concurrency to match CPU cores (${cpus}) is recommended to prevent blocking.`\n } else {\n reason = `Workflow consumes significant memory (${(metrics.memDeltaBytes / 1024 / 1024).toFixed(1)}MB). Concurrency limited by available RAM.`\n }\n\n if (config?.configuredConcurrency && config.configuredConcurrency > recommended) {\n reason += ` \\n⚠️ Warning: Your current setting (${config.configuredConcurrency}) exceeds the recommended limit (${recommended}).`\n }\n\n return {\n type,\n safeConcurrency: safe,\n efficientConcurrency: efficient,\n suggestedConcurrency: `${Math.max(1, Math.floor(recommended * 0.5))} - ${recommended}`,\n reason,\n }\n }\n}\n","/**\n * @fileoverview @gravito/flux - Platform-agnostic Workflow Engine\n *\n * High-performance, type-safe workflow engine with Bun optimizations.\n *\n * @example Basic Usage\n * ```typescript\n * import { FluxEngine, createWorkflow } from '@gravito/flux'\n *\n * const workflow = createWorkflow('order-process')\n * .input<{ orderId: string }>()\n * .step('validate', async (ctx) => {\n * ctx.data.order = await fetchOrder(ctx.input.orderId)\n * })\n * .step('process', async (ctx) => {\n * await processPayment(ctx.data.order)\n * })\n * .commit('notify', async (ctx) => {\n * await sendEmail(ctx.data.order.email)\n * })\n *\n * const engine = new FluxEngine()\n * const result = await engine.execute(workflow, { orderId: '123' })\n * ```\n *\n * @module @gravito/flux\n */\n\n// Builder\nexport { createWorkflow, WorkflowBuilder } from './builder/WorkflowBuilder'\nexport { ContextManager } from './core/ContextManager'\n// Core (for advanced usage)\nexport { StateMachine } from './core/StateMachine'\nexport { StepExecutor } from './core/StepExecutor'\n// Core\nexport { FluxEngine } from './engine/FluxEngine'\n// Logger\nexport { FluxConsoleLogger, FluxSilentLogger } from './logger/FluxLogger'\n// Gravito Integration\nexport { OrbitFlux, type OrbitFluxOptions } from './orbit/OrbitFlux'\n// Profiler\nexport {\n type ProfileMetrics,\n type ProfileRecommendation,\n WorkflowProfiler,\n} from './profiler/WorkflowProfiler'\nexport { BunSQLiteStorage, type BunSQLiteStorageOptions } from './storage/BunSQLiteStorage'\n// Storage\nexport { MemoryStorage } from './storage/MemoryStorage'\n// Trace\nexport { JsonFileTraceSink } from './trace/JsonFileTraceSink'\n\n// Types\nexport type {\n // Config\n FluxConfig,\n // Logger\n FluxLogger,\n FluxResult,\n // Trace\n FluxTraceEvent,\n FluxTraceEventType,\n FluxTraceSink,\n // Helper\n FluxWaitResult,\n // Step types\n StepDefinition,\n StepDescriptor,\n StepExecution,\n StepResult,\n WorkflowContext,\n WorkflowDefinition,\n WorkflowDescriptor,\n WorkflowFilter,\n WorkflowState,\n // Core types\n WorkflowStatus,\n // Storage\n WorkflowStorage,\n} from './types'\n\n/**\n * Flux helper utilities\n */\nexport const Flux = {\n /**\n * Suspend workflow execution and wait for a signal\n *\n * @param signal - Signal name to wait for\n */\n wait: (signal: string): import('./types').FluxWaitResult => ({\n __kind: 'flux_wait',\n signal,\n }),\n}\n"],"mappings":"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","names":["cpus"]}
|
|
1
|
+
{"version":3,"sources":["../src/profiler/WorkflowProfiler.ts","../src/index.ts"],"sourcesContent":["import * as os from 'node:os'\nimport { FluxEngine } from '../engine/FluxEngine'\nimport { FluxSilentLogger } from '../logger/FluxLogger'\nimport type { WorkflowDefinition } from '../types'\n\n/**\n * Performance metrics captured during a workflow profiling session.\n *\n * Used to quantify the resource footprint of a workflow execution, enabling\n * data-driven decisions for infrastructure scaling and concurrency tuning.\n *\n * @public\n */\nexport interface ProfileMetrics {\n /** Total wall-clock duration of the workflow execution in milliseconds. */\n durationMs: number\n /** Amount of time spent in user-space CPU operations in milliseconds. */\n cpuUserMs: number\n /** Amount of time spent in system-space CPU operations in milliseconds. */\n cpuSysMs: number\n /** Estimated heap memory increase during execution in bytes. */\n memDeltaBytes: number\n /** Ratio of CPU time to wall-clock time (0.0 to 1.0+). Higher values indicate CPU-bound tasks. */\n cpuRatio: number\n}\n\n/**\n * Concurrency recommendations generated by the profiler.\n *\n * Provides actionable insights into how many instances of a workflow can safely\n * and efficiently run in parallel on the current hardware.\n *\n * @public\n */\nexport interface ProfileRecommendation {\n /** The identified primary bottleneck type. */\n type: 'IO_BOUND' | 'CPU_BOUND' | 'MEMORY_BOUND'\n /** Maximum safe concurrency considering memory and system stability. */\n safeConcurrency: number\n /** Most efficient concurrency level considering CPU utilization. */\n efficientConcurrency: number\n /** A suggested range for worker concurrency configuration. */\n suggestedConcurrency: string\n /** Detailed explanation for the recommendation. */\n reason: string\n}\n\n/**\n * WorkflowProfiler analyzes workflow performance characteristics.\n *\n * It measures CPU usage, memory consumption, and execution duration to recommend\n * optimal concurrency settings for Gravito Quasar workers or high-throughput consumers.\n *\n * @example\n * ```typescript\n * const profiler = new WorkflowProfiler();\n * const metrics = await profiler.profile(myWorkflow, { input: 'data' });\n * const advice = profiler.recommend(metrics);\n * console.log(`Suggested concurrency: ${advice.suggestedConcurrency}`);\n * ```\n *\n * @public\n */\nexport class WorkflowProfiler {\n /**\n * Initializes the profiler with an optional engine.\n *\n * @param engine - The FluxEngine instance to use for execution. If omitted, a silent engine is created.\n */\n constructor(private engine?: FluxEngine) {\n if (!this.engine) {\n // Default minimal engine for profiling (Silent)\n this.engine = new FluxEngine({\n logger: new FluxSilentLogger(),\n })\n }\n }\n\n /**\n * Executes a workflow and captures its resource consumption metrics.\n *\n * Performs a warmup run to ensure JIT optimization before measurement.\n *\n * @param workflow - The workflow definition to profile.\n * @param input - The input data for the workflow execution.\n * @returns A promise resolving to the captured performance metrics.\n *\n * @example\n * ```typescript\n * const metrics = await profiler.profile(orderWorkflow, { id: '123' });\n * console.log(`Duration: ${metrics.durationMs}ms`);\n * ```\n */\n async profile<TInput>(\n workflow: WorkflowDefinition<TInput, any>,\n input: TInput\n ): Promise<ProfileMetrics> {\n // 1. Warmup (JIT)\n try {\n await this.engine?.execute(workflow, input)\n } catch {}\n\n // 2. Measure\n if (global.gc) {\n global.gc()\n }\n\n const startCpu = process.cpuUsage()\n const startMem = process.memoryUsage().heapUsed\n const startTime = process.hrtime.bigint()\n\n await this.engine?.execute(workflow, input)\n\n const endTime = process.hrtime.bigint()\n const endCpu = process.cpuUsage(startCpu)\n const endMem = process.memoryUsage().heapUsed\n\n // 3. Calculate\n const durationNs = Number(endTime - startTime)\n const durationMs = durationNs / 1_000_000\n const cpuUserMs = endCpu.user / 1000\n const cpuSysMs = endCpu.system / 1000\n const totalCpuMs = cpuUserMs + cpuSysMs\n const memDeltaBytes = Math.max(0, endMem - startMem) // Clamp to 0\n\n // CPU Ratio: How much % of the time was spent on CPU vs Waiting\n const cpuRatio = totalCpuMs / durationMs\n\n return {\n durationMs,\n cpuUserMs,\n cpuSysMs,\n memDeltaBytes,\n cpuRatio,\n }\n }\n\n /**\n * Analyzes metrics to generate concurrency recommendations.\n *\n * Considers system CPU cores and total memory to calculate safe and efficient limits.\n *\n * @param metrics - The metrics captured during a profiling session.\n * @param config - Optional current configuration to check against recommendations.\n * @returns A recommendation object containing bottleneck analysis and concurrency limits.\n *\n * @example\n * ```typescript\n * const advice = profiler.recommend(metrics, { configuredConcurrency: 10 });\n * if (advice.type === 'CPU_BOUND') {\n * console.warn('Workflow is CPU bound, consider reducing concurrency');\n * }\n * ```\n */\n recommend(\n metrics: ProfileMetrics,\n config?: { configuredConcurrency?: number }\n ): ProfileRecommendation {\n const totalMem = os.totalmem()\n const cpus = os.cpus().length\n\n // 1. Analyze Bottleneck Type\n let type: ProfileRecommendation['type'] = 'IO_BOUND'\n if (metrics.cpuRatio > 0.5) {\n type = 'CPU_BOUND'\n } else if (metrics.memDeltaBytes > 50 * 1024 * 1024) {\n // > 50MB per run\n type = 'MEMORY_BOUND'\n }\n\n // 2. Calculate Limits\n\n // Memory Limit: Keep 30% buffer for system, divide rest by per-workflow memory\n const safeMem = totalMem * 0.7\n // Use at least 1MB as baseline to avoid division by zero or huge numbers\n const perInstanceMem = Math.max(metrics.memDeltaBytes, 1024 * 1024)\n const maxMemConcurrency = Math.floor(safeMem / perInstanceMem)\n\n // CPU Limit:\n // If IO Bound (0.2% cpu), we can run many. 100% / 0.2% = 500 tasks per core.\n // We cap efficiency at a reasonable number to avoid Event Loop Lag density.\n const cpuEfficiencyFactor = 1 / Math.max(metrics.cpuRatio, 0.001) // Avoid div by 0\n const maxCpuConcurrency = Math.floor(cpus * cpuEfficiencyFactor)\n\n // 3. Synthesize Recommendation\n const safe = Math.min(maxMemConcurrency, 200) // Hard cap at 200 for sanity\n let efficient = Math.min(maxCpuConcurrency, 200)\n\n // If CPU bound, strict limit based on cores\n if (type === 'CPU_BOUND') {\n efficient = cpus // 1:1 mapping is best for CPU bound\n }\n\n const recommended = Math.min(safe, efficient)\n\n let reason = ''\n if (type === 'IO_BOUND') {\n reason = `Workflow is I/O intensive (CPU usage ${(metrics.cpuRatio * 100).toFixed(1)}%). It is safe to run high concurrency up to ${recommended}.`\n } else if (type === 'CPU_BOUND') {\n reason = `Workflow is CPU intensive. Limiting concurrency to match CPU cores (${cpus}) is recommended to prevent blocking.`\n } else {\n reason = `Workflow consumes significant memory (${(metrics.memDeltaBytes / 1024 / 1024).toFixed(1)}MB). Concurrency limited by available RAM.`\n }\n\n if (config?.configuredConcurrency && config.configuredConcurrency > recommended) {\n reason += ` \\n⚠️ Warning: Your current setting (${config.configuredConcurrency}) exceeds the recommended limit (${recommended}).`\n }\n\n return {\n type,\n safeConcurrency: safe,\n efficientConcurrency: efficient,\n suggestedConcurrency: `${Math.max(1, Math.floor(recommended * 0.5))} - ${recommended}`,\n reason,\n }\n }\n}\n","/**\n * @fileoverview @gravito/flux - Platform-agnostic Workflow Engine\n *\n * High-performance, type-safe workflow engine with Bun optimizations.\n *\n * @example Basic Usage\n * ```typescript\n * import { FluxEngine, createWorkflow } from '@gravito/flux'\n *\n * const workflow = createWorkflow('order-process')\n * .input<{ orderId: string }>()\n * .step('validate', async (ctx) => {\n * ctx.data.order = await fetchOrder(ctx.input.orderId)\n * })\n * .step('process', async (ctx) => {\n * await processPayment(ctx.data.order)\n * })\n * .commit('notify', async (ctx) => {\n * await sendEmail(ctx.data.order.email)\n * })\n *\n * const engine = new FluxEngine()\n * const result = await engine.execute(workflow, { orderId: '123' })\n * ```\n *\n * @module @gravito/flux\n */\n\n// Builder\nexport { createWorkflow, WorkflowBuilder } from './builder/WorkflowBuilder'\nexport { ContextManager } from './core/ContextManager'\nexport { type Lock, type LockProvider, MemoryLockProvider } from './core/LockProvider'\nexport {\n type RedisClient,\n RedisLockProvider,\n type RedisLockProviderOptions,\n} from './core/RedisLockProvider'\n// Core (for advanced usage)\nexport { StateMachine } from './core/StateMachine'\nexport { StepExecutor } from './core/StepExecutor'\nexport {\n type BatchExecutionOptions,\n BatchExecutor,\n type BatchItemResult,\n type BatchResult,\n} from './engine/BatchExecutor'\n// Core\nexport { FluxEngine } from './engine/FluxEngine'\n// Errors\nexport {\n cannotRemoveRoot,\n cannotReplaceRoot,\n emptyWorkflow,\n FluxError,\n FluxErrorCode,\n invalidInput,\n invalidJsonPointer,\n invalidPathTraversal,\n invalidStateTransition,\n invalidStepIndex,\n noRecoveryAction,\n stepNotFound,\n workflowDefinitionChanged,\n workflowNameMismatch,\n workflowNotFound,\n workflowNotSuspended,\n} from './errors'\n// Logger\nexport { FluxConsoleLogger, FluxSilentLogger } from './logger/FluxLogger'\nexport { CronTrigger } from './orbit/CronTrigger'\n// Gravito Integration\nexport { OrbitFlux, type OrbitFluxOptions } from './orbit/OrbitFlux'\n// Profiler\nexport {\n type ProfileMetrics,\n type ProfileRecommendation,\n WorkflowProfiler,\n} from './profiler/WorkflowProfiler'\nexport { BunSQLiteStorage, type BunSQLiteStorageOptions } from './storage/BunSQLiteStorage'\n// Storage\nexport { MemoryStorage } from './storage/MemoryStorage'\nexport { PostgreSQLStorage, type PostgreSQLStorageOptions } from './storage/PostgreSQLStorage'\n// Trace\nexport { JsonFileTraceSink } from './trace/JsonFileTraceSink'\n// Types\nexport type {\n CronScheduleOptions,\n // Config\n FluxConfig,\n // Logger\n FluxLogger,\n FluxResult,\n // Trace\n FluxTraceEvent,\n FluxTraceEventType,\n FluxTraceSink,\n // Helper\n FluxWaitResult,\n // Step types\n StepDefinition,\n StepDescriptor,\n StepExecution,\n StepResult,\n WorkflowContext,\n WorkflowDefinition,\n WorkflowDescriptor,\n WorkflowFilter,\n WorkflowState,\n // Core types\n WorkflowStatus,\n // Storage\n WorkflowStorage,\n} from './types'\n// Visualization\nexport { MermaidGenerator, type MermaidOptions } from './visualization/MermaidGenerator'\n\n/**\n * Flux helper utilities for workflow control flow.\n *\n * Provides methods to interact with the workflow engine's special behaviors,\n * such as suspending execution to wait for external signals.\n */\nexport const Flux = {\n /**\n * Suspends workflow execution until a specific signal is received.\n *\n * When a handler returns this result, the engine saves the current state\n * and stops execution. The workflow can be resumed later using `engine.signal()`.\n *\n * @param signal - The unique identifier for the signal to wait for.\n * @returns A special result object that instructs the engine to suspend.\n *\n * @example\n * ```typescript\n * .step('wait-for-approval', async (ctx) => {\n * return Flux.wait('manager-approval');\n * })\n * ```\n */\n wait: (signal: string): import('./types').FluxWaitResult => ({\n __kind: 'flux_wait',\n signal,\n }),\n}\n"],"mappings":"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","names":["cpus"]}
|