@gravito/flux 1.0.0-alpha.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/README.md +295 -0
- package/dist/builder/WorkflowBuilder.d.ts +96 -0
- package/dist/builder/WorkflowBuilder.d.ts.map +1 -0
- package/dist/builder/index.d.ts +2 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/bun.d.ts +9 -0
- package/dist/bun.d.ts.map +1 -0
- package/dist/bun.js +7 -0
- package/dist/chunk-qjdtqchy.js +145 -0
- package/dist/core/ContextManager.d.ts +40 -0
- package/dist/core/ContextManager.d.ts.map +1 -0
- package/dist/core/StateMachine.d.ts +43 -0
- package/dist/core/StateMachine.d.ts.map +1 -0
- package/dist/core/StepExecutor.d.ts +34 -0
- package/dist/core/StepExecutor.d.ts.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/engine/FluxEngine.d.ts +66 -0
- package/dist/engine/FluxEngine.d.ts.map +1 -0
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +479 -0
- package/dist/index.node.d.ts +18 -0
- package/dist/index.node.d.ts.map +1 -0
- package/dist/logger/FluxLogger.d.ts +40 -0
- package/dist/logger/FluxLogger.d.ts.map +1 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/node/index.cjs +651 -0
- package/dist/node/index.mjs +619 -0
- package/dist/orbit/OrbitFlux.d.ts +107 -0
- package/dist/orbit/OrbitFlux.d.ts.map +1 -0
- package/dist/orbit/index.d.ts +2 -0
- package/dist/orbit/index.d.ts.map +1 -0
- package/dist/storage/BunSQLiteStorage.d.ts +73 -0
- package/dist/storage/BunSQLiteStorage.d.ts.map +1 -0
- package/dist/storage/MemoryStorage.d.ts +28 -0
- package/dist/storage/MemoryStorage.d.ts.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/types.d.ts +194 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StepExecutor.d.ts","sourceRoot":"","sources":["../../src/core/StepExecutor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE1F;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,cAAc,CAAQ;gBAElB,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO;IAK9E;;OAEG;IACG,OAAO,CACX,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,UAAU,CAAC;IA0DtB;;OAEG;YACW,kBAAkB;IAYhC;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Flux Engine - Main workflow execution engine
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates workflow execution with storage and event handling.
|
|
5
|
+
*
|
|
6
|
+
* @module @gravito/flux
|
|
7
|
+
*/
|
|
8
|
+
import { WorkflowBuilder } from '../builder/WorkflowBuilder';
|
|
9
|
+
import type { FluxConfig, FluxResult, WorkflowDefinition, WorkflowStorage } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Flux Engine
|
|
12
|
+
*
|
|
13
|
+
* Main workflow execution engine.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const engine = new FluxEngine({ storage: new MemoryStorage() })
|
|
18
|
+
*
|
|
19
|
+
* const workflow = createWorkflow('process-order')
|
|
20
|
+
* .input<{ orderId: string }>()
|
|
21
|
+
* .step('fetch', async (ctx) => { ... })
|
|
22
|
+
* .step('validate', async (ctx) => { ... })
|
|
23
|
+
* .commit('save', async (ctx) => { ... })
|
|
24
|
+
*
|
|
25
|
+
* const result = await engine.execute(workflow, { orderId: '123' })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class FluxEngine {
|
|
29
|
+
private storage;
|
|
30
|
+
private executor;
|
|
31
|
+
private contextManager;
|
|
32
|
+
private config;
|
|
33
|
+
constructor(config?: FluxConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Execute a workflow with input data
|
|
36
|
+
*
|
|
37
|
+
* @param workflow - Workflow builder or definition
|
|
38
|
+
* @param input - Input data for the workflow
|
|
39
|
+
* @returns Execution result
|
|
40
|
+
*/
|
|
41
|
+
execute<TInput, TData = Record<string, unknown>>(workflow: WorkflowBuilder<TInput> | WorkflowDefinition<TInput>, input: TInput): Promise<FluxResult<TData>>;
|
|
42
|
+
/**
|
|
43
|
+
* Resume a paused or failed workflow
|
|
44
|
+
*
|
|
45
|
+
* @param workflowId - Workflow instance ID
|
|
46
|
+
* @returns Execution result or null if not found
|
|
47
|
+
*/
|
|
48
|
+
resume<TData = Record<string, unknown>>(workflowId: string): Promise<FluxResult<TData> | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Get workflow state by ID
|
|
51
|
+
*/
|
|
52
|
+
get(workflowId: string): Promise<import("..").WorkflowState | null>;
|
|
53
|
+
/**
|
|
54
|
+
* List workflows
|
|
55
|
+
*/
|
|
56
|
+
list(filter?: Parameters<WorkflowStorage['list']>[0]): Promise<import("..").WorkflowState[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize engine (init storage)
|
|
59
|
+
*/
|
|
60
|
+
init(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Shutdown engine (cleanup)
|
|
63
|
+
*/
|
|
64
|
+
close(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=FluxEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FluxEngine.d.ts","sourceRoot":"","sources":["../../src/engine/FluxEngine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAK5D,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EAEV,kBAAkB,EAClB,eAAe,EAChB,MAAM,UAAU,CAAA;AAEjB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,GAAE,UAAe;IAUnC;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnD,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,EAC9D,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAoH7B;;;;;OAKG;IACG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAUpC;;OAEG;IACG,GAAG,CAAC,UAAU,EAAE,MAAM;IAI5B;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAI1D;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview @gravito/flux - Platform-agnostic Workflow Engine
|
|
3
|
+
*
|
|
4
|
+
* High-performance, type-safe workflow engine with Bun optimizations.
|
|
5
|
+
*
|
|
6
|
+
* @example Basic Usage
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { FluxEngine, createWorkflow } from '@gravito/flux'
|
|
9
|
+
*
|
|
10
|
+
* const workflow = createWorkflow('order-process')
|
|
11
|
+
* .input<{ orderId: string }>()
|
|
12
|
+
* .step('validate', async (ctx) => {
|
|
13
|
+
* ctx.data.order = await fetchOrder(ctx.input.orderId)
|
|
14
|
+
* })
|
|
15
|
+
* .step('process', async (ctx) => {
|
|
16
|
+
* await processPayment(ctx.data.order)
|
|
17
|
+
* })
|
|
18
|
+
* .commit('notify', async (ctx) => {
|
|
19
|
+
* await sendEmail(ctx.data.order.email)
|
|
20
|
+
* })
|
|
21
|
+
*
|
|
22
|
+
* const engine = new FluxEngine()
|
|
23
|
+
* const result = await engine.execute(workflow, { orderId: '123' })
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @module @gravito/flux
|
|
27
|
+
*/
|
|
28
|
+
export { createWorkflow, WorkflowBuilder } from './builder/WorkflowBuilder';
|
|
29
|
+
export { ContextManager } from './core/ContextManager';
|
|
30
|
+
export { StateMachine } from './core/StateMachine';
|
|
31
|
+
export { StepExecutor } from './core/StepExecutor';
|
|
32
|
+
export { FluxEngine } from './engine/FluxEngine';
|
|
33
|
+
export { FluxConsoleLogger, FluxSilentLogger } from './logger/FluxLogger';
|
|
34
|
+
export { OrbitFlux, type OrbitFluxOptions } from './orbit/OrbitFlux';
|
|
35
|
+
export { BunSQLiteStorage, type BunSQLiteStorageOptions } from './storage/BunSQLiteStorage';
|
|
36
|
+
export { MemoryStorage } from './storage/MemoryStorage';
|
|
37
|
+
export type { FluxConfig, FluxLogger, FluxResult, StepDefinition, StepExecution, StepResult, WorkflowContext, WorkflowDefinition, WorkflowFilter, WorkflowState, WorkflowStatus, WorkflowStorage, } from './types';
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEhD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEzE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,4BAA4B,CAAA;AAE3F,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAGvD,YAAY,EAEV,UAAU,EAEV,UAAU,EACV,UAAU,EAEV,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EAEb,cAAc,EAEd,eAAe,GAChB,MAAM,SAAS,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
BunSQLiteStorage
|
|
4
|
+
} from "./chunk-qjdtqchy.js";
|
|
5
|
+
|
|
6
|
+
// src/builder/WorkflowBuilder.ts
|
|
7
|
+
class WorkflowBuilder {
|
|
8
|
+
_name;
|
|
9
|
+
_steps = [];
|
|
10
|
+
_validateInput;
|
|
11
|
+
constructor(name) {
|
|
12
|
+
this._name = name;
|
|
13
|
+
}
|
|
14
|
+
input() {
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
validate(validator) {
|
|
18
|
+
this._validateInput = validator;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
step(name, handler, options) {
|
|
22
|
+
this._steps.push({
|
|
23
|
+
name,
|
|
24
|
+
handler,
|
|
25
|
+
retries: options?.retries,
|
|
26
|
+
timeout: options?.timeout,
|
|
27
|
+
when: options?.when,
|
|
28
|
+
commit: false
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
commit(name, handler, options) {
|
|
33
|
+
this._steps.push({
|
|
34
|
+
name,
|
|
35
|
+
handler,
|
|
36
|
+
retries: options?.retries,
|
|
37
|
+
timeout: options?.timeout,
|
|
38
|
+
when: options?.when,
|
|
39
|
+
commit: true
|
|
40
|
+
});
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
build() {
|
|
44
|
+
if (this._steps.length === 0) {
|
|
45
|
+
throw new Error(`Workflow "${this._name}" has no steps`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
name: this._name,
|
|
49
|
+
steps: [...this._steps],
|
|
50
|
+
validateInput: this._validateInput
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
get name() {
|
|
54
|
+
return this._name;
|
|
55
|
+
}
|
|
56
|
+
get stepCount() {
|
|
57
|
+
return this._steps.length;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function createWorkflow(name) {
|
|
61
|
+
return new WorkflowBuilder(name);
|
|
62
|
+
}
|
|
63
|
+
// src/core/ContextManager.ts
|
|
64
|
+
function generateId() {
|
|
65
|
+
return crypto.randomUUID();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class ContextManager {
|
|
69
|
+
create(name, input, stepCount) {
|
|
70
|
+
const history = Array.from({ length: stepCount }, (_, _i) => ({
|
|
71
|
+
name: "",
|
|
72
|
+
status: "pending",
|
|
73
|
+
retries: 0
|
|
74
|
+
}));
|
|
75
|
+
return {
|
|
76
|
+
id: generateId(),
|
|
77
|
+
name,
|
|
78
|
+
input,
|
|
79
|
+
data: {},
|
|
80
|
+
status: "pending",
|
|
81
|
+
currentStep: 0,
|
|
82
|
+
history
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
restore(state) {
|
|
86
|
+
return {
|
|
87
|
+
id: state.id,
|
|
88
|
+
name: state.name,
|
|
89
|
+
input: state.input,
|
|
90
|
+
data: { ...state.data },
|
|
91
|
+
status: state.status,
|
|
92
|
+
currentStep: state.currentStep,
|
|
93
|
+
history: state.history.map((h) => ({ ...h }))
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
toState(ctx) {
|
|
97
|
+
return {
|
|
98
|
+
id: ctx.id,
|
|
99
|
+
name: ctx.name,
|
|
100
|
+
status: ctx.status,
|
|
101
|
+
input: ctx.input,
|
|
102
|
+
data: { ...ctx.data },
|
|
103
|
+
currentStep: ctx.currentStep,
|
|
104
|
+
history: ctx.history.map((h) => ({ ...h })),
|
|
105
|
+
createdAt: new Date,
|
|
106
|
+
updatedAt: new Date
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
updateStatus(ctx, status) {
|
|
110
|
+
return {
|
|
111
|
+
...ctx,
|
|
112
|
+
status
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
advanceStep(ctx) {
|
|
116
|
+
return {
|
|
117
|
+
...ctx,
|
|
118
|
+
currentStep: ctx.currentStep + 1
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
setStepName(ctx, index, name) {
|
|
122
|
+
if (ctx.history[index]) {
|
|
123
|
+
ctx.history[index].name = name;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// src/core/StateMachine.ts
|
|
128
|
+
var TRANSITIONS = {
|
|
129
|
+
pending: ["running", "failed"],
|
|
130
|
+
running: ["paused", "completed", "failed"],
|
|
131
|
+
paused: ["running", "failed"],
|
|
132
|
+
completed: [],
|
|
133
|
+
failed: ["pending"]
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
class StateMachine extends EventTarget {
|
|
137
|
+
_status = "pending";
|
|
138
|
+
get status() {
|
|
139
|
+
return this._status;
|
|
140
|
+
}
|
|
141
|
+
canTransition(to) {
|
|
142
|
+
return TRANSITIONS[this._status].includes(to);
|
|
143
|
+
}
|
|
144
|
+
transition(to) {
|
|
145
|
+
if (!this.canTransition(to)) {
|
|
146
|
+
throw new Error(`Invalid state transition: ${this._status} \u2192 ${to}`);
|
|
147
|
+
}
|
|
148
|
+
const from = this._status;
|
|
149
|
+
this._status = to;
|
|
150
|
+
this.dispatchEvent(new CustomEvent("transition", {
|
|
151
|
+
detail: { from, to }
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
forceStatus(status) {
|
|
155
|
+
this._status = status;
|
|
156
|
+
}
|
|
157
|
+
isTerminal() {
|
|
158
|
+
return this._status === "completed" || this._status === "failed";
|
|
159
|
+
}
|
|
160
|
+
canExecute() {
|
|
161
|
+
return this._status === "pending" || this._status === "paused";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// src/core/StepExecutor.ts
|
|
165
|
+
class StepExecutor {
|
|
166
|
+
defaultRetries;
|
|
167
|
+
defaultTimeout;
|
|
168
|
+
constructor(options = {}) {
|
|
169
|
+
this.defaultRetries = options.defaultRetries ?? 3;
|
|
170
|
+
this.defaultTimeout = options.defaultTimeout ?? 30000;
|
|
171
|
+
}
|
|
172
|
+
async execute(step, ctx, execution) {
|
|
173
|
+
const maxRetries = step.retries ?? this.defaultRetries;
|
|
174
|
+
const timeout = step.timeout ?? this.defaultTimeout;
|
|
175
|
+
const startTime = Date.now();
|
|
176
|
+
if (step.when && !step.when(ctx)) {
|
|
177
|
+
execution.status = "skipped";
|
|
178
|
+
return {
|
|
179
|
+
success: true,
|
|
180
|
+
duration: 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
execution.status = "running";
|
|
184
|
+
execution.startedAt = new Date;
|
|
185
|
+
let lastError;
|
|
186
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
187
|
+
execution.retries = attempt;
|
|
188
|
+
try {
|
|
189
|
+
await this.executeWithTimeout(step.handler, ctx, timeout);
|
|
190
|
+
execution.status = "completed";
|
|
191
|
+
execution.completedAt = new Date;
|
|
192
|
+
execution.duration = Date.now() - startTime;
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
duration: execution.duration
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
199
|
+
if (attempt < maxRetries) {
|
|
200
|
+
await this.sleep(Math.min(1000 * 2 ** attempt, 1e4));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
execution.status = "failed";
|
|
205
|
+
execution.completedAt = new Date;
|
|
206
|
+
execution.duration = Date.now() - startTime;
|
|
207
|
+
execution.error = lastError?.message;
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: lastError,
|
|
211
|
+
duration: execution.duration
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
async executeWithTimeout(handler, ctx, timeout) {
|
|
215
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
216
|
+
setTimeout(() => reject(new Error("Step timeout")), timeout);
|
|
217
|
+
});
|
|
218
|
+
await Promise.race([Promise.resolve(handler(ctx)), timeoutPromise]);
|
|
219
|
+
}
|
|
220
|
+
sleep(ms) {
|
|
221
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// src/storage/MemoryStorage.ts
|
|
225
|
+
class MemoryStorage {
|
|
226
|
+
store = new Map;
|
|
227
|
+
async save(state) {
|
|
228
|
+
this.store.set(state.id, {
|
|
229
|
+
...state,
|
|
230
|
+
updatedAt: new Date
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async load(id) {
|
|
234
|
+
return this.store.get(id) ?? null;
|
|
235
|
+
}
|
|
236
|
+
async list(filter) {
|
|
237
|
+
let results = Array.from(this.store.values());
|
|
238
|
+
if (filter?.name) {
|
|
239
|
+
results = results.filter((s) => s.name === filter.name);
|
|
240
|
+
}
|
|
241
|
+
if (filter?.status) {
|
|
242
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
243
|
+
results = results.filter((s) => statuses.includes(s.status));
|
|
244
|
+
}
|
|
245
|
+
results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
246
|
+
if (filter?.offset) {
|
|
247
|
+
results = results.slice(filter.offset);
|
|
248
|
+
}
|
|
249
|
+
if (filter?.limit) {
|
|
250
|
+
results = results.slice(0, filter.limit);
|
|
251
|
+
}
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
254
|
+
async delete(id) {
|
|
255
|
+
this.store.delete(id);
|
|
256
|
+
}
|
|
257
|
+
async init() {}
|
|
258
|
+
async close() {
|
|
259
|
+
this.store.clear();
|
|
260
|
+
}
|
|
261
|
+
size() {
|
|
262
|
+
return this.store.size;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/engine/FluxEngine.ts
|
|
267
|
+
class FluxEngine {
|
|
268
|
+
storage;
|
|
269
|
+
executor;
|
|
270
|
+
contextManager;
|
|
271
|
+
config;
|
|
272
|
+
constructor(config = {}) {
|
|
273
|
+
this.config = config;
|
|
274
|
+
this.storage = config.storage ?? new MemoryStorage;
|
|
275
|
+
this.executor = new StepExecutor({
|
|
276
|
+
defaultRetries: config.defaultRetries,
|
|
277
|
+
defaultTimeout: config.defaultTimeout
|
|
278
|
+
});
|
|
279
|
+
this.contextManager = new ContextManager;
|
|
280
|
+
}
|
|
281
|
+
async execute(workflow, input) {
|
|
282
|
+
const startTime = Date.now();
|
|
283
|
+
const definition = workflow instanceof WorkflowBuilder ? workflow.build() : workflow;
|
|
284
|
+
if (definition.validateInput && !definition.validateInput(input)) {
|
|
285
|
+
throw new Error(`Invalid input for workflow "${definition.name}"`);
|
|
286
|
+
}
|
|
287
|
+
const ctx = this.contextManager.create(definition.name, input, definition.steps.length);
|
|
288
|
+
const stateMachine = new StateMachine;
|
|
289
|
+
await this.storage.save(this.contextManager.toState(ctx));
|
|
290
|
+
try {
|
|
291
|
+
stateMachine.transition("running");
|
|
292
|
+
Object.assign(ctx, { status: "running" });
|
|
293
|
+
for (let i = 0;i < definition.steps.length; i++) {
|
|
294
|
+
const step = definition.steps[i];
|
|
295
|
+
const execution = ctx.history[i];
|
|
296
|
+
this.contextManager.setStepName(ctx, i, step.name);
|
|
297
|
+
Object.assign(ctx, { currentStep: i });
|
|
298
|
+
this.config.on?.stepStart?.(step.name, ctx);
|
|
299
|
+
const result = await this.executor.execute(step, ctx, execution);
|
|
300
|
+
if (result.success) {
|
|
301
|
+
this.config.on?.stepComplete?.(step.name, ctx, result);
|
|
302
|
+
} else {
|
|
303
|
+
this.config.on?.stepError?.(step.name, ctx, result.error);
|
|
304
|
+
stateMachine.transition("failed");
|
|
305
|
+
Object.assign(ctx, { status: "failed" });
|
|
306
|
+
await this.storage.save({
|
|
307
|
+
...this.contextManager.toState(ctx),
|
|
308
|
+
error: result.error?.message
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
id: ctx.id,
|
|
312
|
+
status: "failed",
|
|
313
|
+
data: ctx.data,
|
|
314
|
+
history: ctx.history,
|
|
315
|
+
duration: Date.now() - startTime,
|
|
316
|
+
error: result.error
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
await this.storage.save(this.contextManager.toState(ctx));
|
|
320
|
+
}
|
|
321
|
+
stateMachine.transition("completed");
|
|
322
|
+
Object.assign(ctx, { status: "completed" });
|
|
323
|
+
await this.storage.save({
|
|
324
|
+
...this.contextManager.toState(ctx),
|
|
325
|
+
completedAt: new Date
|
|
326
|
+
});
|
|
327
|
+
this.config.on?.workflowComplete?.(ctx);
|
|
328
|
+
return {
|
|
329
|
+
id: ctx.id,
|
|
330
|
+
status: "completed",
|
|
331
|
+
data: ctx.data,
|
|
332
|
+
history: ctx.history,
|
|
333
|
+
duration: Date.now() - startTime
|
|
334
|
+
};
|
|
335
|
+
} catch (error) {
|
|
336
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
337
|
+
this.config.on?.workflowError?.(ctx, err);
|
|
338
|
+
stateMachine.forceStatus("failed");
|
|
339
|
+
Object.assign(ctx, { status: "failed" });
|
|
340
|
+
await this.storage.save({
|
|
341
|
+
...this.contextManager.toState(ctx),
|
|
342
|
+
error: err.message
|
|
343
|
+
});
|
|
344
|
+
return {
|
|
345
|
+
id: ctx.id,
|
|
346
|
+
status: "failed",
|
|
347
|
+
data: ctx.data,
|
|
348
|
+
history: ctx.history,
|
|
349
|
+
duration: Date.now() - startTime,
|
|
350
|
+
error: err
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async resume(workflowId) {
|
|
355
|
+
const state = await this.storage.load(workflowId);
|
|
356
|
+
if (!state) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
throw new Error("Resume not yet implemented");
|
|
360
|
+
}
|
|
361
|
+
async get(workflowId) {
|
|
362
|
+
return this.storage.load(workflowId);
|
|
363
|
+
}
|
|
364
|
+
async list(filter) {
|
|
365
|
+
return this.storage.list(filter);
|
|
366
|
+
}
|
|
367
|
+
async init() {
|
|
368
|
+
await this.storage.init?.();
|
|
369
|
+
}
|
|
370
|
+
async close() {
|
|
371
|
+
await this.storage.close?.();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// src/logger/FluxLogger.ts
|
|
375
|
+
class FluxConsoleLogger {
|
|
376
|
+
prefix;
|
|
377
|
+
constructor(prefix = "[Flux]") {
|
|
378
|
+
this.prefix = prefix;
|
|
379
|
+
}
|
|
380
|
+
debug(message, ...args) {
|
|
381
|
+
console.debug(`${this.prefix} ${message}`, ...args);
|
|
382
|
+
}
|
|
383
|
+
info(message, ...args) {
|
|
384
|
+
console.info(`${this.prefix} ${message}`, ...args);
|
|
385
|
+
}
|
|
386
|
+
warn(message, ...args) {
|
|
387
|
+
console.warn(`${this.prefix} ${message}`, ...args);
|
|
388
|
+
}
|
|
389
|
+
error(message, ...args) {
|
|
390
|
+
console.error(`${this.prefix} ${message}`, ...args);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
class FluxSilentLogger {
|
|
395
|
+
debug() {}
|
|
396
|
+
info() {}
|
|
397
|
+
warn() {}
|
|
398
|
+
error() {}
|
|
399
|
+
}
|
|
400
|
+
// src/orbit/OrbitFlux.ts
|
|
401
|
+
class OrbitFlux {
|
|
402
|
+
options;
|
|
403
|
+
engine;
|
|
404
|
+
constructor(options = {}) {
|
|
405
|
+
this.options = {
|
|
406
|
+
storage: "memory",
|
|
407
|
+
exposeAs: "flux",
|
|
408
|
+
defaultRetries: 3,
|
|
409
|
+
defaultTimeout: 30000,
|
|
410
|
+
...options
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
static configure(options = {}) {
|
|
414
|
+
return new OrbitFlux(options);
|
|
415
|
+
}
|
|
416
|
+
async install(core) {
|
|
417
|
+
const { storage, dbPath, exposeAs, defaultRetries, defaultTimeout, logger } = this.options;
|
|
418
|
+
let storageAdapter;
|
|
419
|
+
if (typeof storage === "string") {
|
|
420
|
+
switch (storage) {
|
|
421
|
+
case "sqlite":
|
|
422
|
+
storageAdapter = new BunSQLiteStorage({ path: dbPath });
|
|
423
|
+
break;
|
|
424
|
+
default:
|
|
425
|
+
storageAdapter = new MemoryStorage;
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
storageAdapter = storage;
|
|
429
|
+
}
|
|
430
|
+
await storageAdapter.init?.();
|
|
431
|
+
const engineConfig = {
|
|
432
|
+
storage: storageAdapter,
|
|
433
|
+
defaultRetries,
|
|
434
|
+
defaultTimeout,
|
|
435
|
+
logger: logger ?? {
|
|
436
|
+
debug: (msg) => core.logger.debug(`[Flux] ${msg}`),
|
|
437
|
+
info: (msg) => core.logger.info(`[Flux] ${msg}`),
|
|
438
|
+
warn: (msg) => core.logger.warn(`[Flux] ${msg}`),
|
|
439
|
+
error: (msg) => core.logger.error(`[Flux] ${msg}`)
|
|
440
|
+
},
|
|
441
|
+
on: {
|
|
442
|
+
stepStart: (step) => {
|
|
443
|
+
core.hooks.doAction("flux:step:start", { step });
|
|
444
|
+
},
|
|
445
|
+
stepComplete: (step, ctx, result) => {
|
|
446
|
+
core.hooks.doAction("flux:step:complete", { step, ctx, result });
|
|
447
|
+
},
|
|
448
|
+
stepError: (step, ctx, error) => {
|
|
449
|
+
core.hooks.doAction("flux:step:error", { step, ctx, error });
|
|
450
|
+
},
|
|
451
|
+
workflowComplete: (ctx) => {
|
|
452
|
+
core.hooks.doAction("flux:workflow:complete", { ctx });
|
|
453
|
+
},
|
|
454
|
+
workflowError: (ctx, error) => {
|
|
455
|
+
core.hooks.doAction("flux:workflow:error", { ctx, error });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
this.engine = new FluxEngine(engineConfig);
|
|
460
|
+
core.services.set(exposeAs, this.engine);
|
|
461
|
+
core.logger.info(`[OrbitFlux] Initialized (Storage: ${typeof storage === "string" ? storage : "custom"})`);
|
|
462
|
+
}
|
|
463
|
+
getEngine() {
|
|
464
|
+
return this.engine;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
export {
|
|
468
|
+
createWorkflow,
|
|
469
|
+
WorkflowBuilder,
|
|
470
|
+
StepExecutor,
|
|
471
|
+
StateMachine,
|
|
472
|
+
OrbitFlux,
|
|
473
|
+
MemoryStorage,
|
|
474
|
+
FluxSilentLogger,
|
|
475
|
+
FluxEngine,
|
|
476
|
+
FluxConsoleLogger,
|
|
477
|
+
ContextManager,
|
|
478
|
+
BunSQLiteStorage
|
|
479
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Node.js compatible entry point
|
|
3
|
+
*
|
|
4
|
+
* This entry exports only platform-agnostic components.
|
|
5
|
+
* BunSQLiteStorage is NOT included (use main entry for Bun).
|
|
6
|
+
*
|
|
7
|
+
* @module @gravito/flux
|
|
8
|
+
*/
|
|
9
|
+
export { createWorkflow, WorkflowBuilder } from './builder/WorkflowBuilder';
|
|
10
|
+
export { FluxEngine } from './engine/FluxEngine';
|
|
11
|
+
export { MemoryStorage } from './storage/MemoryStorage';
|
|
12
|
+
export { ContextManager } from './core/ContextManager';
|
|
13
|
+
export { StateMachine } from './core/StateMachine';
|
|
14
|
+
export { StepExecutor } from './core/StepExecutor';
|
|
15
|
+
export { FluxConsoleLogger, FluxSilentLogger } from './logger/FluxLogger';
|
|
16
|
+
export { OrbitFlux, type OrbitFluxOptions } from './orbit/OrbitFlux';
|
|
17
|
+
export type { FluxConfig, FluxLogger, FluxResult, StepDefinition, StepExecution, StepResult, WorkflowContext, WorkflowDefinition, WorkflowFilter, WorkflowState, WorkflowStatus, WorkflowStorage, } from './types';
|
|
18
|
+
//# sourceMappingURL=index.node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.node.d.ts","sourceRoot":"","sources":["../src/index.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3E,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAGhD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAIvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEzE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAGpE,YAAY,EAEV,UAAU,EAEV,UAAU,EACV,UAAU,EAEV,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EAEb,cAAc,EAEd,eAAe,GAChB,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Console Logger for FluxEngine
|
|
3
|
+
*
|
|
4
|
+
* Default logger implementation using console.
|
|
5
|
+
*
|
|
6
|
+
* @module @gravito/flux
|
|
7
|
+
*/
|
|
8
|
+
import type { FluxLogger } from '../types';
|
|
9
|
+
/**
|
|
10
|
+
* Console Logger
|
|
11
|
+
*
|
|
12
|
+
* Default logger that outputs to console.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const engine = new FluxEngine({
|
|
17
|
+
* logger: new FluxConsoleLogger()
|
|
18
|
+
* })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class FluxConsoleLogger implements FluxLogger {
|
|
22
|
+
private prefix;
|
|
23
|
+
constructor(prefix?: string);
|
|
24
|
+
debug(message: string, ...args: unknown[]): void;
|
|
25
|
+
info(message: string, ...args: unknown[]): void;
|
|
26
|
+
warn(message: string, ...args: unknown[]): void;
|
|
27
|
+
error(message: string, ...args: unknown[]): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Silent Logger
|
|
31
|
+
*
|
|
32
|
+
* Logger that outputs nothing (for testing or production).
|
|
33
|
+
*/
|
|
34
|
+
export declare class FluxSilentLogger implements FluxLogger {
|
|
35
|
+
debug(): void;
|
|
36
|
+
info(): void;
|
|
37
|
+
warn(): void;
|
|
38
|
+
error(): void;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=FluxLogger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FluxLogger.d.ts","sourceRoot":"","sources":["../../src/logger/FluxLogger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,YAAW,UAAU;IAClD,OAAO,CAAC,MAAM,CAAQ;gBAEV,MAAM,SAAW;IAI7B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAIhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;CAGjD;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,KAAK,IAAI,IAAI;IACb,IAAI,IAAI,IAAI;IACZ,IAAI,IAAI,IAAI;IACZ,KAAK,IAAI,IAAI;CACd"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
|